/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2007 - 2013 Red Hat, Inc.
 * Copyright (C) 2007 - 2008 Novell, Inc.
 */

#include "libnm-core-impl/nm-default-libnm-core.h"

#include "nm-setting-8021x.h"

#include "libnm-glib-aux/nm-secret-utils.h"
#include "libnm-crypto/nm-crypto.h"
#include "nm-utils.h"
#include "nm-utils-private.h"
#include "nm-setting-private.h"
#include "nm-core-enum-types.h"

/**
 * SECTION:nm-setting-8021x
 * @short_description: Describes 802.1x-authenticated connection properties
 *
 * The #NMSetting8021x object is a #NMSetting subclass that describes
 * properties necessary for connection to 802.1x-authenticated networks, such as
 * WPA and WPA2 Enterprise Wi-Fi networks and wired 802.1x networks.  802.1x
 * connections typically use certificates and/or EAP authentication methods to
 * securely verify, identify, and authenticate the client to the network itself,
 * instead of simply relying on a widely shared static key.
 *
 * It's a good idea to read up on wpa_supplicant configuration before using this
 * setting extensively, since most of the options here correspond closely with
 * the relevant wpa_supplicant configuration options.
 *
 * Furthermore, to get a good idea of 802.1x, EAP, TLS, TTLS, etc and their
 * applications to Wi-Fi and wired networks, you'll want to get copies of the
 * following books.
 *
 *  802.11 Wireless Networks: The Definitive Guide, Second Edition
 *       Author: Matthew Gast
 *       ISBN: 978-0596100520
 *
 *  Cisco Wireless LAN Security
 *       Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky
 *       ISBN: 978-1587051548
 **/

/*****************************************************************************/

static NMSetting8021xCKFormat
_crypto_format_to_ck(NMCryptoFileFormat format)
{
    G_STATIC_ASSERT((NM_SETTING_802_1X_CK_FORMAT_UNKNOWN
                     == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN));
    G_STATIC_ASSERT(
        (NM_SETTING_802_1X_CK_FORMAT_X509 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509));
    G_STATIC_ASSERT((NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
                     == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY));
    G_STATIC_ASSERT((NM_SETTING_802_1X_CK_FORMAT_PKCS12
                     == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12));

    nm_assert(NM_IN_SET(format,
                        NM_CRYPTO_FILE_FORMAT_UNKNOWN,
                        NM_CRYPTO_FILE_FORMAT_X509,
                        NM_CRYPTO_FILE_FORMAT_RAW_KEY,
                        NM_CRYPTO_FILE_FORMAT_PKCS12));
    return (NMSetting8021xCKFormat) format;
}

/*****************************************************************************/

typedef void (*EAPMethodNeedSecretsFunc)(NMSetting8021x *self,
                                         GPtrArray      *secrets,
                                         gboolean        phase2,
                                         gboolean        check_rerequest);

typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, gboolean phase2, GError **error);

typedef struct {
    const char              *method;
    EAPMethodNeedSecretsFunc ns_func;
    EAPMethodValidateFunc    v_func;
} EAPMethodsTable;

static const EAPMethodsTable eap_methods_table[];

/*****************************************************************************/

NM_GOBJECT_PROPERTIES_DEFINE(NMSetting8021x,
                             PROP_EAP,
                             PROP_IDENTITY,
                             PROP_ANONYMOUS_IDENTITY,
                             PROP_PAC_FILE,
                             PROP_CA_CERT,
                             PROP_CA_CERT_PASSWORD,
                             PROP_CA_CERT_PASSWORD_FLAGS,
                             PROP_CA_PATH,
                             PROP_SUBJECT_MATCH,
                             PROP_ALTSUBJECT_MATCHES,
                             PROP_DOMAIN_SUFFIX_MATCH,
                             PROP_DOMAIN_MATCH,
                             PROP_CLIENT_CERT,
                             PROP_CLIENT_CERT_PASSWORD,
                             PROP_CLIENT_CERT_PASSWORD_FLAGS,
                             PROP_PHASE1_PEAPVER,
                             PROP_PHASE1_PEAPLABEL,
                             PROP_PHASE1_FAST_PROVISIONING,
                             PROP_PHASE1_AUTH_FLAGS,
                             PROP_PHASE2_AUTH,
                             PROP_PHASE2_AUTHEAP,
                             PROP_PHASE2_CA_CERT,
                             PROP_PHASE2_CA_CERT_PASSWORD,
                             PROP_PHASE2_CA_CERT_PASSWORD_FLAGS,
                             PROP_PHASE2_CA_PATH,
                             PROP_PHASE2_SUBJECT_MATCH,
                             PROP_PHASE2_ALTSUBJECT_MATCHES,
                             PROP_PHASE2_DOMAIN_SUFFIX_MATCH,
                             PROP_PHASE2_DOMAIN_MATCH,
                             PROP_PHASE2_CLIENT_CERT,
                             PROP_PHASE2_CLIENT_CERT_PASSWORD,
                             PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
                             PROP_PASSWORD,
                             PROP_PASSWORD_FLAGS,
                             PROP_PASSWORD_RAW,
                             PROP_PASSWORD_RAW_FLAGS,
                             PROP_PRIVATE_KEY,
                             PROP_PRIVATE_KEY_PASSWORD,
                             PROP_PRIVATE_KEY_PASSWORD_FLAGS,
                             PROP_PHASE2_PRIVATE_KEY,
                             PROP_PHASE2_PRIVATE_KEY_PASSWORD,
                             PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
                             PROP_PIN,
                             PROP_PIN_FLAGS,
                             PROP_SYSTEM_CA_CERTS,
                             PROP_OPTIONAL,
                             PROP_AUTH_TIMEOUT, );

typedef struct {
    GSList *eap; /* GSList of strings */
    char   *identity;
    char   *anonymous_identity;
    char   *pac_file;
    GBytes *ca_cert;
    char   *ca_cert_password;
    char   *ca_path;
    char   *subject_match;
    GSList *altsubject_matches;
    char   *domain_suffix_match;
    char   *domain_match;
    GBytes *client_cert;
    char   *client_cert_password;
    char   *phase1_peapver;
    char   *phase1_peaplabel;
    char   *phase1_fast_provisioning;
    char   *phase2_auth;
    char   *phase2_autheap;
    GBytes *phase2_ca_cert;
    char   *phase2_ca_cert_password;
    char   *phase2_ca_path;
    char   *phase2_subject_match;
    GSList *phase2_altsubject_matches;
    char   *phase2_domain_suffix_match;
    char   *phase2_domain_match;
    GBytes *phase2_client_cert;
    char   *phase2_client_cert_password;
    char   *password;
    GBytes *password_raw;
    char   *pin;
    GBytes *private_key;
    char   *private_key_password;
    GBytes *phase2_private_key;
    char   *phase2_private_key_password;
    guint   ca_cert_password_flags;
    guint   client_cert_password_flags;
    guint   phase2_ca_cert_password_flags;
    guint   phase2_client_cert_password_flags;
    guint   password_flags;
    guint   password_raw_flags;
    guint   pin_flags;
    guint   private_key_password_flags;
    guint   phase2_private_key_password_flags;
    guint32 phase1_auth_flags;
    gint32  auth_timeout;
    bool    optional;
    bool    system_ca_certs;
} NMSetting8021xPrivate;

/**
 * NMSetting8021x:
 *
 * IEEE 802.1x Authentication Settings
 */
struct _NMSetting8021x {
    NMSetting             parent;
    NMSetting8021xPrivate _priv;
};

struct _NMSetting8021xClass {
    NMSettingClass parent;
};

G_DEFINE_TYPE(NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING)

#define NM_SETTING_802_1X_GET_PRIVATE(o) \
    _NM_GET_PRIVATE(o, NMSetting8021x, NM_IS_SETTING_802_1X, NMSetting)

/*****************************************************************************/

/**
 * nm_setting_802_1x_check_cert_scheme:
 * @pdata: (nullable): the data pointer
 * @length: the length of the data
 * @error: validation reason
 *
 * Determines and verifies the blob type.
 * When setting certificate properties of NMSetting8021x
 * the blob must be not UNKNOWN (or NULL).
 *
 * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
 * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
 *
 * Since: 1.2
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_check_cert_scheme(gconstpointer pdata, gsize length, GError **error)
{
    const char            *data = pdata;
    NMSetting8021xCKScheme scheme;
    gsize                  prefix_length;

    g_return_val_if_fail(!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);

    if (!length || !data) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("binary data missing"));
        return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
    }

    if (length >= NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)
        && !memcmp(data,
                   NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH,
                   NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) {
        scheme        = NM_SETTING_802_1X_CK_SCHEME_PATH;
        prefix_length = NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
    } else if (length >= NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11)
               && !memcmp(data,
                          NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11,
                          NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11))) {
        scheme        = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
        prefix_length = NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11);
    } else {
        scheme        = NM_SETTING_802_1X_CK_SCHEME_BLOB;
        prefix_length = 0;
    }

    if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
        /* An actual URI must be NUL terminated, contain at least
         * one non-NUL character, and contain only one trailing NUL
         * character.
         * And ensure it's UTF-8 valid too so we can pass it through
         * D-Bus and stuff like that. */

        if (data[length - 1] != '\0') {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("URI not NUL terminated"));
            return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
        }
        length--;

        if (length <= prefix_length) {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("URI is empty"));
            return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
        }

        if (!g_utf8_validate(data + prefix_length, length - prefix_length, NULL)) {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("URI is not valid UTF-8"));
            return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
        }
    }

    return scheme;
}

NMSetting8021xCKScheme
_nm_setting_802_1x_cert_get_scheme(GBytes *bytes, GError **error)
{
    const char *data;
    gsize       length;

    if (!bytes) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("data missing"));
        return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
    }

    data = g_bytes_get_data(bytes, &length);
    return nm_setting_802_1x_check_cert_scheme(data, length, error);
}

static gboolean
_cert_verify_scheme(NMSetting8021xCKScheme scheme, GBytes *bytes, GError **error)
{
    GError                *local = NULL;
    NMSetting8021xCKScheme scheme_detected;

    nm_assert(bytes);

    scheme_detected = _nm_setting_802_1x_cert_get_scheme(bytes, &local);
    if (scheme_detected == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
        g_set_error(error,
                    NM_CONNECTION_ERROR,
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
                    _("certificate is invalid: %s"),
                    local->message);
        return FALSE;
    }

    if (scheme_detected != scheme) {
        g_set_error(error,
                    NM_CONNECTION_ERROR,
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
                    _("certificate detected as invalid scheme"));
        return FALSE;
    }

    return TRUE;
}

GBytes *
_nm_setting_802_1x_cert_value_to_bytes(NMSetting8021xCKScheme scheme,
                                       const guint8          *val_bin,
                                       gssize                 val_len,
                                       GError               **error)
{
    gs_unref_bytes GBytes *bytes = NULL;
    guint8                *mem;
    gsize                  total_len;

    nm_assert(val_bin);

    switch (scheme) {
    case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
        if (val_len < 0)
            val_len = strlen((char *) val_bin) + 1;

        bytes = g_bytes_new(val_bin, val_len);
        break;
    case NM_SETTING_802_1X_CK_SCHEME_PATH:
        if (val_len < 0)
            val_len = strlen((char *) val_bin) + 1;

        total_len = NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + ((gsize) val_len);

        mem = g_new(guint8, total_len);
        memcpy(mem,
               NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH,
               NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
        memcpy(&mem[NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], val_bin, val_len);
        bytes = g_bytes_new_take(mem, total_len);
        break;
    default:
        g_return_val_if_reached(NULL);
    }

    if (!_cert_verify_scheme(scheme, bytes, error))
        return NULL;

    return g_steal_pointer(&bytes);
}

static const char *
_cert_get_path(GBytes *bytes)
{
    const guint8 *bin;

    nm_assert(bytes);
    nm_assert(g_bytes_get_size(bytes) >= NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));

    bin = g_bytes_get_data(bytes, NULL);

    nm_assert(bin);
    nm_assert(bin[g_bytes_get_size(bytes) - 1] == '\0');
    nm_assert(g_str_has_prefix((const char *) bin, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));

    return (const char *) &bin[NM_STRLEN(NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)];
}

#define _cert_assert_scheme(cert, check_scheme, ret_val)           \
    G_STMT_START                                                   \
    {                                                              \
        NMSetting8021xCKScheme scheme;                             \
                                                                   \
        scheme = _nm_setting_802_1x_cert_get_scheme((cert), NULL); \
        if (scheme != check_scheme) {                              \
            g_return_val_if_fail(scheme == check_scheme, ret_val); \
            return ret_val;                                        \
        }                                                          \
    }                                                              \
    G_STMT_END

#define _cert_impl_get_scheme(setting, cert_field)                                                 \
    G_STMT_START                                                                                   \
    {                                                                                              \
        NMSetting8021x *const _setting = (setting);                                                \
        GBytes               *_cert;                                                               \
                                                                                                   \
        g_return_val_if_fail(NM_IS_SETTING_802_1X(_setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); \
                                                                                                   \
        _cert = NM_SETTING_802_1X_GET_PRIVATE(_setting)->cert_field;                               \
                                                                                                   \
        return _nm_setting_802_1x_cert_get_scheme(_cert, NULL);                                    \
    }                                                                                              \
    G_STMT_END

#define _cert_impl_get_blob(setting, cert_field)                            \
    G_STMT_START                                                            \
    {                                                                       \
        NMSetting8021x *const _setting = (setting);                         \
        GBytes               *_cert;                                        \
                                                                            \
        g_return_val_if_fail(NM_IS_SETTING_802_1X(_setting), NULL);         \
                                                                            \
        _cert = NM_SETTING_802_1X_GET_PRIVATE(_setting)->cert_field;        \
                                                                            \
        _cert_assert_scheme(_cert, NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); \
                                                                            \
        return _cert;                                                       \
    }                                                                       \
    G_STMT_END

#define _cert_impl_get_path(setting, cert_field)                            \
    G_STMT_START                                                            \
    {                                                                       \
        NMSetting8021x *const _setting = (setting);                         \
        GBytes               *_cert;                                        \
                                                                            \
        g_return_val_if_fail(NM_IS_SETTING_802_1X(_setting), NULL);         \
                                                                            \
        _cert = NM_SETTING_802_1X_GET_PRIVATE(_setting)->cert_field;        \
                                                                            \
        _cert_assert_scheme(_cert, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); \
                                                                            \
        return _cert_get_path(_cert);                                       \
    }                                                                       \
    G_STMT_END

#define _cert_impl_get_uri(setting, cert_field)                               \
    G_STMT_START                                                              \
    {                                                                         \
        NMSetting8021x *const _setting = (setting);                           \
        GBytes               *_cert;                                          \
                                                                              \
        g_return_val_if_fail(NM_IS_SETTING_802_1X(_setting), NULL);           \
                                                                              \
        _cert = NM_SETTING_802_1X_GET_PRIVATE(_setting)->cert_field;          \
                                                                              \
        _cert_assert_scheme(_cert, NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); \
                                                                              \
        return g_bytes_get_data(_cert, NULL);                                 \
    }                                                                         \
    G_STMT_END

static gboolean
_cert_impl_set(NMSetting8021x         *setting,
               _PropertyEnums          property,
               const char             *value,
               const char             *password,
               NMSetting8021xCKScheme  scheme,
               NMSetting8021xCKFormat *out_format,
               GError                **error)
{
    NMSetting8021xPrivate *priv;
    NMCryptoFileFormat     format             = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
    gs_unref_bytes GBytes *cert               = NULL;
    GBytes               **p_cert             = NULL;
    GBytes               **p_client_cert      = NULL;
    char                 **p_password         = NULL;
    _PropertyEnums         notify_cert        = property;
    _PropertyEnums         notify_password    = PROP_0;
    _PropertyEnums         notify_client_cert = PROP_0;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(!error || !*error, FALSE);
    if (value) {
        g_return_val_if_fail(g_utf8_validate(value, -1, NULL), FALSE);
        g_return_val_if_fail(NM_IN_SET(scheme,
                                       NM_SETTING_802_1X_CK_SCHEME_BLOB,
                                       NM_SETTING_802_1X_CK_SCHEME_PATH,
                                       NM_SETTING_802_1X_CK_SCHEME_PKCS11),
                             FALSE);
    }

    if (!value) {
        /* coerce password to %NULL. It should be already. */
        password = NULL;
    }

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);

    if (!value) {
        /* pass. */
    } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
        cert = _nm_setting_802_1x_cert_value_to_bytes(scheme, (guint8 *) value, -1, error);
        if (!cert)
            goto err;
    } else {
        gs_unref_bytes GBytes *file = NULL;

        if (NM_IN_SET(property, PROP_PRIVATE_KEY, PROP_PHASE2_PRIVATE_KEY)) {
            file = nm_utils_read_crypto_file_to_bytes(value, error);
            if (!file)
                goto err;
            format = nm_crypto_verify_private_key_data(g_bytes_get_data(file, NULL),
                                                       g_bytes_get_size(file),
                                                       password,
                                                       NULL,
                                                       error);
            if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN)
                goto err;
        } else {
            if (!nm_crypto_load_and_verify_certificate(value, &format, &file, error))
                goto err;
        }

        nm_assert(format != NM_CRYPTO_FILE_FORMAT_UNKNOWN);
        nm_assert(file);

        if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
            cert = g_steal_pointer(&file);
            if (!_cert_verify_scheme(scheme, cert, error))
                goto err;
        } else {
            cert = _nm_setting_802_1x_cert_value_to_bytes(scheme, (guint8 *) value, -1, error);
            if (!cert)
                goto err;
        }
    }

    switch (property) {
    case PROP_CA_CERT:
    case PROP_PHASE2_CA_CERT:
        if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11
            && format != NM_CRYPTO_FILE_FORMAT_X509) {
            /* wpa_supplicant can only use raw x509 CA certs */
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("CA certificate must be in X.509 format"));
            goto err;
        }
        p_cert = (property == PROP_CA_CERT) ? &priv->ca_cert : &priv->phase2_ca_cert;
        break;
    case PROP_CLIENT_CERT:
    case PROP_PHASE2_CLIENT_CERT:
        if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11
            && !NM_IN_SET(format, NM_CRYPTO_FILE_FORMAT_X509, NM_CRYPTO_FILE_FORMAT_PKCS12)) {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("invalid certificate format"));
            goto err;
        }
        p_cert = (property == PROP_CLIENT_CERT) ? &priv->client_cert : &priv->phase2_client_cert;
        break;
    case PROP_PRIVATE_KEY:
        p_cert             = &priv->private_key;
        p_password         = &priv->private_key_password;
        p_client_cert      = &priv->client_cert;
        notify_password    = PROP_PRIVATE_KEY_PASSWORD;
        notify_client_cert = PROP_CLIENT_CERT;
        break;
    case PROP_PHASE2_PRIVATE_KEY:
        p_cert             = &priv->phase2_private_key;
        p_password         = &priv->phase2_private_key_password;
        p_client_cert      = &priv->phase2_client_cert;
        notify_password    = PROP_PHASE2_PRIVATE_KEY_PASSWORD;
        notify_client_cert = PROP_PHASE2_CLIENT_CERT;
        break;
    default:
        nm_assert_not_reached();
        break;
    }

    /* As required by NM and wpa_supplicant, set the client-cert
     * property to the same PKCS#12 data.
     */
    if (cert && p_client_cert && format == NM_CRYPTO_FILE_FORMAT_PKCS12
        && !nm_g_bytes_equal0(cert, *p_client_cert)) {
        g_bytes_unref(*p_client_cert);
        *p_client_cert = g_bytes_ref(cert);
    } else
        notify_client_cert = PROP_0;

    if (p_cert && !nm_g_bytes_equal0(cert, *p_cert)) {
        g_bytes_unref(*p_cert);
        *p_cert = g_steal_pointer(&cert);
    } else
        notify_cert = PROP_0;

    if (p_password && !nm_streq0(password, *p_password)) {
        nm_free_secret(*p_password);
        *p_password = g_strdup(password);
    } else
        notify_password = PROP_0;

    nm_gobject_notify_together(setting, notify_cert, notify_password, notify_client_cert);

    NM_SET_OUT(out_format, _crypto_format_to_ck(format));
    return TRUE;

err:
    g_prefix_error(error,
                   "%s.%s: ",
                   NM_SETTING_802_1X_SETTING_NAME,
                   obj_properties[property]->name);
    NM_SET_OUT(out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
    return FALSE;
}

static NMSetting8021xCKFormat
_cert_impl_get_key_format_from_bytes(GBytes *private_key)
{
    const char *path;
    GError     *error = NULL;

    if (!private_key)
        return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;

    switch (_nm_setting_802_1x_cert_get_scheme(private_key, NULL)) {
    case NM_SETTING_802_1X_CK_SCHEME_BLOB:
        if (nm_crypto_is_pkcs12_data(g_bytes_get_data(private_key, NULL),
                                     g_bytes_get_size(private_key),
                                     NULL))
            return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
        return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
    case NM_SETTING_802_1X_CK_SCHEME_PATH:
        path = _cert_get_path(private_key);
        if (nm_crypto_is_pkcs12_file(path, &error))
            return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
        if (error && error->domain == G_FILE_ERROR) {
            g_error_free(error);
            return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
        }
        g_error_free(error);
        return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
    default:
        break;
    }

    return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
}
#define _cert_impl_get_key_format(setting, private_key_field)                                      \
    ({                                                                                             \
        NMSetting8021x        *_setting = (setting);                                               \
        NMSetting8021xPrivate *_priv;                                                              \
                                                                                                   \
        g_return_val_if_fail(NM_IS_SETTING_802_1X(_setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); \
                                                                                                   \
        _priv = NM_SETTING_802_1X_GET_PRIVATE(_setting);                                           \
        _cert_impl_get_key_format_from_bytes(_priv->private_key_field);                            \
    })

static gboolean
_cert_verify_property(GBytes     *bytes,
                      const char *prop_name,
                      const char *password,
                      const char *password_prop_name,
                      GError    **error)
{
    GError                *local = NULL;
    NMSetting8021xCKScheme scheme;

    if (!bytes)
        return TRUE;

    scheme = _nm_setting_802_1x_cert_get_scheme(bytes, &local);
    if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
        g_set_error(error,
                    NM_CONNECTION_ERROR,
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
                    _("certificate is invalid: %s"),
                    local->message);
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name);
        g_error_free(local);
        return FALSE;
    }

    if (password && (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)) {
        g_set_error(error,
                    NM_CONNECTION_ERROR,
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
                    _("password is not supported when certificate is not on a PKCS#11 token"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, password_prop_name);
        return FALSE;
    }

    return TRUE;
}

/*****************************************************************************/

/**
 * nm_setting_802_1x_get_num_eap_methods:
 * @setting: the #NMSetting8021x
 *
 * Returns the number of eap methods allowed for use when connecting to the
 * network.  Generally only one EAP method is used.  Use the functions
 * nm_setting_802_1x_get_eap_method(), nm_setting_802_1x_add_eap_method(),
 * and nm_setting_802_1x_remove_eap_method() for adding, removing, and retrieving
 * allowed EAP methods.
 *
 * Returns: the number of allowed EAP methods
 **/
guint32
nm_setting_802_1x_get_num_eap_methods(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), 0);

    return g_slist_length(NM_SETTING_802_1X_GET_PRIVATE(setting)->eap);
}

/**
 * nm_setting_802_1x_get_eap_method:
 * @setting: the #NMSetting8021x
 * @i: the index of the EAP method name to return
 *
 * Returns the name of the allowed EAP method at index @i.
 *
 * Returns: the name of the allowed EAP method at index @i
 **/
const char *
nm_setting_802_1x_get_eap_method(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_return_val_if_fail(i <= g_slist_length(priv->eap), NULL);

    return (const char *) g_slist_nth_data(priv->eap, i);
}

/**
 * nm_setting_802_1x_add_eap_method:
 * @setting: the #NMSetting8021x
 * @eap: the name of the EAP method to allow for this connection
 *
 * Adds an allowed EAP method.  The setting is not valid until at least one
 * EAP method has been added.  See #NMSetting8021x:eap property for a list of
 * allowed EAP methods.
 *
 * Returns: %TRUE if the EAP method was successfully added, %FALSE if it was
 *  not a valid method or if it was already allowed.
 **/
gboolean
nm_setting_802_1x_add_eap_method(NMSetting8021x *setting, const char *eap)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(eap != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->eap; iter; iter = g_slist_next(iter)) {
        if (!strcmp(eap, (char *) iter->data))
            return FALSE;
    }

    priv->eap = g_slist_append(priv->eap, g_ascii_strdown(eap, -1));
    _notify(setting, PROP_EAP);
    return TRUE;
}

/**
 * nm_setting_802_1x_remove_eap_method:
 * @setting: the #NMSetting8021x
 * @i: the index of the EAP method to remove
 *
 * Removes the allowed EAP method at the specified index.
 **/
void
nm_setting_802_1x_remove_eap_method(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;
    GSList                *elt;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    elt  = g_slist_nth(priv->eap, i);
    g_return_if_fail(elt != NULL);

    g_free(elt->data);
    priv->eap = g_slist_delete_link(priv->eap, elt);
    _notify(setting, PROP_EAP);
}

/**
 * nm_setting_802_1x_remove_eap_method_by_value:
 * @setting: the #NMSetting8021x
 * @eap: the name of the EAP method to remove
 *
 * Removes the allowed EAP method @method.
 *
 * Returns: %TRUE if the EAP method was founs and removed, %FALSE if it was not.
 **/
gboolean
nm_setting_802_1x_remove_eap_method_by_value(NMSetting8021x *setting, const char *eap)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(eap != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->eap; iter; iter = g_slist_next(iter)) {
        if (!strcmp(eap, (char *) iter->data)) {
            priv->eap = g_slist_delete_link(priv->eap, iter);
            _notify(setting, PROP_EAP);
            return TRUE;
        }
    }
    return FALSE;
}

/**
 * nm_setting_802_1x_clear_eap_methods:
 * @setting: the #NMSetting8021x
 *
 * Clears all allowed EAP methods.
 **/
void
nm_setting_802_1x_clear_eap_methods(NMSetting8021x *setting)
{
    NMSetting8021xPrivate *priv;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_slist_free_full(priv->eap, g_free);
    priv->eap = NULL;
    _notify(setting, PROP_EAP);
}

/**
 * nm_setting_802_1x_get_identity:
 * @setting: the #NMSetting8021x
 *
 * Returns the identifier used by some EAP methods (like TLS) to
 * authenticate the user.  Often this is a username or login name.
 *
 * Returns: the user identifier
 **/
const char *
nm_setting_802_1x_get_identity(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->identity;
}

/**
 * nm_setting_802_1x_get_anonymous_identity:
 * @setting: the #NMSetting8021x
 *
 * Returns the anonymous identifier used by some EAP methods (like TTLS) to
 * authenticate the user in the outer unencrypted "phase 1" authentication.  The
 * inner "phase 2" authentication will use the #NMSetting8021x:identity in
 * a secure form, if applicable for that EAP method.
 *
 * Returns: the anonymous identifier
 **/
const char *
nm_setting_802_1x_get_anonymous_identity(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->anonymous_identity;
}

/**
 * nm_setting_802_1x_get_pac_file:
 * @setting: the #NMSetting8021x
 *
 * Returns the file containing PAC credentials used by EAP-FAST method.
 *
 * Returns: the PAC file
 **/
const char *
nm_setting_802_1x_get_pac_file(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->pac_file;
}

/**
 * nm_setting_802_1x_get_ca_path:
 * @setting: the #NMSetting8021x
 *
 * Returns the path of the CA certificate directory if previously set.  Systems
 * will often have a directory that contains multiple individual CA certificates
 * which the supplicant can then add to the verification chain.  This may be
 * used in addition to the #NMSetting8021x:ca-cert property to add more CA
 * certificates for verifying the network to client.
 *
 * Returns: the CA certificate directory path
 **/
const char *
nm_setting_802_1x_get_ca_path(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->ca_path;
}

/**
 * nm_setting_802_1x_get_system_ca_certs:
 * @setting: the #NMSetting8021x
 *
 * Sets the #NMSetting8021x:system-ca-certs property. The
 * #NMSetting8021x:ca-path and #NMSetting8021x:phase2-ca-path
 * properties are ignored if the #NMSetting8021x:system-ca-certs property is
 * %TRUE, in which case a system-wide CA certificate directory specified at
 * compile time (using the --system-ca-path configure option) is used in place
 * of these properties.
 *
 * Returns: %TRUE if a system CA certificate path should be used, %FALSE if not
 **/
gboolean
nm_setting_802_1x_get_system_ca_certs(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->system_ca_certs;
}

/**
 * nm_setting_802_1x_get_ca_cert_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the CA certificate.  If the returned scheme
 * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_ca_cert_blob();
 * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path();
 * if %NM_SETTING_802_1X_CK_SCHEME_PKCS11, use nm_setting_802_1x_get_ca_cert_uri().
 *
 * Returns: scheme used to store the CA certificate (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_ca_cert_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, ca_cert);
}

/**
 * nm_setting_802_1x_get_ca_cert_blob:
 * @setting: the #NMSetting8021x
 *
 * Returns the CA certificate blob if the CA certificate is stored using the
 * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.  Not all EAP methods use a
 * CA certificate (LEAP for example), and those that can take advantage of the
 * CA certificate allow it to be unset.  Note that lack of a CA certificate
 * reduces security by allowing man-in-the-middle attacks, because the identity
 * of the network cannot be confirmed by the client.
 *
 * Returns: (transfer none): the CA certificate data
 **/
GBytes *
nm_setting_802_1x_get_ca_cert_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, ca_cert);
}

/**
 * nm_setting_802_1x_get_ca_cert_path:
 * @setting: the #NMSetting8021x
 *
 * Returns the CA certificate path if the CA certificate is stored using the
 * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.  Not all EAP methods use a
 * CA certificate (LEAP for example), and those that can take advantage of the
 * CA certificate allow it to be unset.  Note that lack of a CA certificate
 * reduces security by allowing man-in-the-middle attacks, because the identity
 * of the network cannot be confirmed by the client.
 *
 * Returns: path to the CA certificate file
 **/
const char *
nm_setting_802_1x_get_ca_cert_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, ca_cert);
}

/**
 * nm_setting_802_1x_get_ca_cert_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the CA certificate URI analogously to
 * nm_setting_802_1x_get_ca_cert_blob() and
 * nm_setting_802_1x_get_ca_cert_path().
 *
 * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_ca_cert_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, ca_cert);
}

/**
 * nm_setting_802_1x_set_ca_cert:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
 *   or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the CA certificate
 *   file (PEM or DER format).  The path must be UTF-8 encoded; use
 *   g_filename_to_utf8() to convert if needed.  Passing %NULL with any @scheme
 *   clears the CA certificate.
 * @scheme: desired storage scheme for the certificate
 * @out_format: on successful return, the type of the certificate added
 * @error: on unsuccessful return, an error
 *
 * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property
 * with the raw certificate data if using the %NM_SETTING_802_1X_CK_SCHEME_BLOB
 * scheme, or with the path to the certificate file if using the
 * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_ca_cert(NMSetting8021x         *setting,
                              const char             *value,
                              NMSetting8021xCKScheme  scheme,
                              NMSetting8021xCKFormat *out_format,
                              GError                **error)
{
    return _cert_impl_set(setting, PROP_CA_CERT, value, NULL, scheme, out_format, error);
}

/**
 * nm_setting_802_1x_get_ca_cert_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the password used to access the CA certificate stored in
 * #NMSetting8021x:ca-cert property. Only makes sense if the certificate
 * is stored on a PKCS#<!-- -->11 token that requires a login.
 *
 * Since: 1.8
 **/
const char *
nm_setting_802_1x_get_ca_cert_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->ca_cert_password;
}

/**
 * nm_setting_802_1x_get_ca_cert_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:ca-cert-password
 *
 * Since: 1.8
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_ca_cert_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->ca_cert_password_flags;
}

/**
 * nm_setting_802_1x_get_subject_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:subject-match property. This is the
 * substring to be matched against the subject of the authentication
 * server certificate, or %NULL no subject verification is to be
 * performed.
 **/
const char *
nm_setting_802_1x_get_subject_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->subject_match;
}

/**
 * nm_setting_802_1x_get_num_altsubject_matches:
 * @setting: the #NMSetting8021x
 *
 * Returns the number of entries in the
 * #NMSetting8021x:altsubject-matches property of this setting.
 *
 * Returns: the number of altsubject-matches entries.
 **/
guint32
nm_setting_802_1x_get_num_altsubject_matches(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), 0);

    return g_slist_length(NM_SETTING_802_1X_GET_PRIVATE(setting)->altsubject_matches);
}

/**
 * nm_setting_802_1x_get_altsubject_match:
 * @setting: the #NMSettingConnection
 * @i: the zero-based index of the array of altSubjectName matches
 *
 * Returns the altSubjectName match at index @i.
 *
 * Returns: the altSubjectName match at index @i
 **/
const char *
nm_setting_802_1x_get_altsubject_match(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_return_val_if_fail(i <= g_slist_length(priv->altsubject_matches), NULL);

    return (const char *) g_slist_nth_data(priv->altsubject_matches, i);
}

/**
 * nm_setting_802_1x_add_altsubject_match:
 * @setting: the #NMSetting8021x
 * @altsubject_match: the altSubjectName to allow for this connection
 *
 * Adds an allowed alternate subject name match.  Until at least one
 * match is added, the altSubjectName of the remote authentication
 * server is not verified.
 *
 * Returns: %TRUE if the alternative subject name match was
 *  successfully added, %FALSE if it was already allowed.
 **/
gboolean
nm_setting_802_1x_add_altsubject_match(NMSetting8021x *setting, const char *altsubject_match)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(altsubject_match != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->altsubject_matches; iter; iter = g_slist_next(iter)) {
        if (!strcmp(altsubject_match, (char *) iter->data))
            return FALSE;
    }

    priv->altsubject_matches = g_slist_append(priv->altsubject_matches, g_strdup(altsubject_match));
    _notify(setting, PROP_ALTSUBJECT_MATCHES);
    return TRUE;
}

/**
 * nm_setting_802_1x_remove_altsubject_match:
 * @setting: the #NMSetting8021x
 * @i: the index of the altSubjectName match to remove
 *
 * Removes the allowed altSubjectName at the specified index.
 **/
void
nm_setting_802_1x_remove_altsubject_match(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;
    GSList                *elt;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    elt  = g_slist_nth(priv->altsubject_matches, i);
    g_return_if_fail(elt != NULL);

    g_free(elt->data);
    priv->altsubject_matches = g_slist_delete_link(priv->altsubject_matches, elt);
    _notify(setting, PROP_ALTSUBJECT_MATCHES);
}

/**
 * nm_setting_802_1x_remove_altsubject_match_by_value:
 * @setting: the #NMSetting8021x
 * @altsubject_match: the altSubjectName to remove
 *
 * Removes the allowed altSubjectName @altsubject_match.
 *
 * Returns: %TRUE if the alternative subject name match was found and removed,
 *          %FALSE if it was not.
 **/
gboolean
nm_setting_802_1x_remove_altsubject_match_by_value(NMSetting8021x *setting,
                                                   const char     *altsubject_match)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(altsubject_match != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->altsubject_matches; iter; iter = g_slist_next(iter)) {
        if (!strcmp(altsubject_match, (char *) iter->data)) {
            priv->altsubject_matches = g_slist_delete_link(priv->altsubject_matches, iter);
            _notify(setting, PROP_ALTSUBJECT_MATCHES);
            return TRUE;
        }
    }
    return FALSE;
}

/**
 * nm_setting_802_1x_clear_altsubject_matches:
 * @setting: the #NMSetting8021x
 *
 * Clears all altSubjectName matches.
 **/
void
nm_setting_802_1x_clear_altsubject_matches(NMSetting8021x *setting)
{
    NMSetting8021xPrivate *priv;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_slist_free_full(priv->altsubject_matches, g_free);
    priv->altsubject_matches = NULL;
    _notify(setting, PROP_ALTSUBJECT_MATCHES);
}

/**
 * nm_setting_802_1x_get_domain_suffix_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:domain-suffix-match property.
 *
 * Since: 1.2
 **/
const char *
nm_setting_802_1x_get_domain_suffix_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->domain_suffix_match;
}

/**
 * nm_setting_802_1x_get_domain_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:domain-match property.
 *
 * Since: 1.24
 **/
const char *
nm_setting_802_1x_get_domain_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->domain_match;
}

/**
 * nm_setting_802_1x_get_client_cert_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the client certificate.  If the returned scheme
 * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_client_cert_blob();
 * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_client_cert_path();
 * if %NM_SETTING_802_1X_CK_SCHEME_PKCS11, use nm_setting_802_1x_get_client_cert_uri().
 *
 * Returns: scheme used to store the client certificate (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_client_cert_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, client_cert);
}

/**
 * nm_setting_802_1x_get_client_cert_blob:
 * @setting: the #NMSetting8021x
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: (transfer none): the client certificate data
 **/
GBytes *
nm_setting_802_1x_get_client_cert_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, client_cert);
}

/**
 * nm_setting_802_1x_get_client_cert_path:
 * @setting: the #NMSetting8021x
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: path to the client certificate file
 **/
const char *
nm_setting_802_1x_get_client_cert_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, client_cert);
}

/**
 * nm_setting_802_1x_get_client_cert_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the client certificate URI analogously to
 * nm_setting_802_1x_get_client_cert_blob() and
 * nm_setting_802_1x_get_client_cert_path().
 *
 * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_client_cert_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, client_cert);
}

/**
 * nm_setting_802_1x_set_client_cert:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
 *   or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the client
 *   certificate file (PEM, DER, or PKCS#<!-- -->12 format).  The path must be UTF-8
 *   encoded; use g_filename_to_utf8() to convert if needed.  Passing %NULL with
 *   any @scheme clears the client certificate.
 * @scheme: desired storage scheme for the certificate
 * @out_format: on successful return, the type of the certificate added
 * @error: on unsuccessful return, an error
 *
 * Reads a certificate from disk and sets the #NMSetting8021x:client-cert
 * property with the raw certificate data if using the
 * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
 * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_client_cert(NMSetting8021x         *setting,
                                  const char             *value,
                                  NMSetting8021xCKScheme  scheme,
                                  NMSetting8021xCKFormat *out_format,
                                  GError                **error)
{
    return _cert_impl_set(setting, PROP_CLIENT_CERT, value, NULL, scheme, out_format, error);
}

/**
 * nm_setting_802_1x_get_client_cert_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the password used to access the client certificate stored in
 * #NMSetting8021x:client-cert property. Only makes sense if the certificate
 * is stored on a PKCS#<!-- -->11 token that requires a login.
 *
 * Since: 1.8
 **/
const char *
nm_setting_802_1x_get_client_cert_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->client_cert_password;
}

/**
 * nm_setting_802_1x_get_client_cert_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:client-cert-password
 *
 * Since: 1.8
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_client_cert_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->client_cert_password_flags;
}

/**
 * nm_setting_802_1x_get_phase1_peapver:
 * @setting: the #NMSetting8021x
 *
 * Returns: the "phase 1" PEAP version to be used when authenticating with
 *  EAP-PEAP as contained in the #NMSetting8021x:phase1-peapver property.  Valid
 *  values are %NULL (unset), "0" (PEAP version 0), and "1" (PEAP version 1).
 **/
const char *
nm_setting_802_1x_get_phase1_peapver(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase1_peapver;
}

/**
 * nm_setting_802_1x_get_phase1_peaplabel:
 * @setting: the #NMSetting8021x
 *
 * Returns: whether the "phase 1" PEAP label is new-style or old-style, to be
 *  used when authenticating with EAP-PEAP, as contained in the
 *  #NMSetting8021x:phase1-peaplabel property.  Valid values are %NULL (unset),
 *  "0" (use old-style label), and "1" (use new-style label).  See the
 *  wpa_supplicant documentation for more details.
 **/
const char *
nm_setting_802_1x_get_phase1_peaplabel(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase1_peaplabel;
}

/**
 * nm_setting_802_1x_get_phase1_fast_provisioning:
 * @setting: the #NMSetting8021x
 *
 * Returns: whether "phase 1" PEAP fast provisioning should be used, as specified
 *  by the #NMSetting8021x:phase1-fast-provisioning property.  See the
 *  wpa_supplicant documentation for more details.
 **/
const char *
nm_setting_802_1x_get_phase1_fast_provisioning(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase1_fast_provisioning;
}

/**
 * nm_setting_802_1x_get_phase1_auth_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the authentication flags for "phase 1".
 *
 * Since: 1.8
 */
NMSetting8021xAuthFlags
nm_setting_802_1x_get_phase1_auth_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), 0);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase1_auth_flags;
}

/**
 * nm_setting_802_1x_get_phase2_auth:
 * @setting: the #NMSetting8021x
 *
 * Returns: the "phase 2" non-EAP (ex MD5) allowed authentication method as
 *   specified by the #NMSetting8021x:phase2-auth property.
 **/
const char *
nm_setting_802_1x_get_phase2_auth(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_auth;
}

/**
 * nm_setting_802_1x_get_phase2_autheap:
 * @setting: the #NMSetting8021x
 *
 * Returns: the "phase 2" EAP-based (ex TLS) allowed authentication method as
 *   specified by the #NMSetting8021x:phase2-autheap property.
 **/
const char *
nm_setting_802_1x_get_phase2_autheap(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_autheap;
}

/**
 * nm_setting_802_1x_get_phase2_ca_path:
 * @setting: the #NMSetting8021x
 *
 * Returns the path of the "phase 2" CA certificate directory if previously set.
 * Systems will often have a directory that contains multiple individual CA
 * certificates which the supplicant can then add to the verification chain.
 * This may be used in addition to the #NMSetting8021x:phase2-ca-cert property
 * to add more CA certificates for verifying the network to client.
 *
 * Returns: the "phase 2" CA certificate directory path
 **/
const char *
nm_setting_802_1x_get_phase2_ca_path(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_ca_path;
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the "phase 2" CA certificate.  If the
 * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
 * nm_setting_802_1x_get_ca_cert_blob(); if %NM_SETTING_802_1X_CK_SCHEME_PATH,
 * use nm_setting_802_1x_get_ca_cert_path(); if %NM_SETTING_802_1X_CK_SCHEME_PKCS11,
 * use nm_setting_802_1x_get_ca_cert_uri().
 *
 * Returns: scheme used to store the "phase 2" CA certificate (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_phase2_ca_cert_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, phase2_ca_cert);
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_blob:
 * @setting: the #NMSetting8021x
 *
 * Returns the "phase 2" CA certificate blob if the CA certificate is stored
 * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.  Not all EAP methods use
 * a CA certificate (LEAP for example), and those that can take advantage of the
 * CA certificate allow it to be unset.  Note that lack of a CA certificate
 * reduces security by allowing man-in-the-middle attacks, because the identity
 * of the network cannot be confirmed by the client.
 *
 * Returns: (transfer none): the "phase 2" CA certificate data
 **/
GBytes *
nm_setting_802_1x_get_phase2_ca_cert_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, phase2_ca_cert);
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_path:
 * @setting: the #NMSetting8021x
 *
 * Returns the "phase 2" CA certificate path if the CA certificate is stored
 * using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.  Not all EAP methods use
 * a CA certificate (LEAP for example), and those that can take advantage of the
 * CA certificate allow it to be unset.  Note that lack of a CA certificate
 * reduces security by allowing man-in-the-middle attacks, because the identity
 * of the network cannot be confirmed by the client.
 *
 * Returns: path to the "phase 2" CA certificate file
 **/
const char *
nm_setting_802_1x_get_phase2_ca_cert_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, phase2_ca_cert);
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the "phase 2" CA certificate URI analogously to
 * nm_setting_802_1x_get_phase2_ca_cert_blob() and
 * nm_setting_802_1x_get_phase2_ca_cert_path().
 *
 * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_phase2_ca_cert_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, phase2_ca_cert);
}

/**
 * nm_setting_802_1x_set_phase2_ca_cert:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
 *   or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" CA
 *   certificate file (PEM or DER format).  The path must be UTF-8 encoded; use
 *   g_filename_to_utf8() to convert if needed.  Passing %NULL with any @scheme
 *   clears the "phase2" CA certificate.
 * @scheme: desired storage scheme for the certificate
 * @out_format: on successful return, the type of the certificate added
 * @error: on unsuccessful return, an error
 *
 * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert
 * property with the raw certificate data if using the
 * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
 * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_phase2_ca_cert(NMSetting8021x         *setting,
                                     const char             *value,
                                     NMSetting8021xCKScheme  scheme,
                                     NMSetting8021xCKFormat *out_format,
                                     GError                **error)
{
    return _cert_impl_set(setting, PROP_PHASE2_CA_CERT, value, NULL, scheme, out_format, error);
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the password used to access the "phase2" CA certificate stored in
 * #NMSetting8021x:phase2-ca-cert property. Only makes sense if the certificate
 * is stored on a PKCS#<!-- -->11 token that requires a login.
 *
 * Since: 1.8
 **/
const char *
nm_setting_802_1x_get_phase2_ca_cert_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_ca_cert_password;
}

/**
 * nm_setting_802_1x_get_phase2_ca_cert_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:phase2-private-key-password
 *
 * Since: 1.8
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_phase2_ca_cert_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_ca_cert_password_flags;
}

/**
 * nm_setting_802_1x_get_phase2_subject_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:phase2-subject-match property. This is
 * the substring to be matched against the subject of the "phase 2"
 * authentication server certificate, or %NULL no subject verification
 * is to be performed.
 **/
const char *
nm_setting_802_1x_get_phase2_subject_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_subject_match;
}

/**
 * nm_setting_802_1x_get_num_phase2_altsubject_matches:
 * @setting: the #NMSetting8021x
 *
 * Returns the number of entries in the
 * #NMSetting8021x:phase2-altsubject-matches property of this setting.
 *
 * Returns: the number of phase2-altsubject-matches entries.
 **/
guint32
nm_setting_802_1x_get_num_phase2_altsubject_matches(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), 0);

    return g_slist_length(NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_altsubject_matches);
}

/**
 * nm_setting_802_1x_get_phase2_domain_suffix_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:phase2-domain-suffix-match property.
 *
 * Since: 1.2
 **/
const char *
nm_setting_802_1x_get_phase2_domain_suffix_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_domain_suffix_match;
}

/**
 * nm_setting_802_1x_get_phase2_domain_match:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSetting8021x:phase2-domain-match property.
 *
 * Since: 1.24
 **/
const char *
nm_setting_802_1x_get_phase2_domain_match(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_domain_match;
}

/**
 * nm_setting_802_1x_get_phase2_altsubject_match:
 * @setting: the #NMSettingConnection
 * @i: the zero-based index of the array of "phase 2" altSubjectName matches
 *
 * Returns the "phase 2" altSubjectName match at index @i.
 *
 * Returns: the "phase 2" altSubjectName match at index @i
 **/
const char *
nm_setting_802_1x_get_phase2_altsubject_match(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_return_val_if_fail(i <= g_slist_length(priv->phase2_altsubject_matches), NULL);

    return (const char *) g_slist_nth_data(priv->phase2_altsubject_matches, i);
}

/**
 * nm_setting_802_1x_add_phase2_altsubject_match:
 * @setting: the #NMSetting8021x
 * @phase2_altsubject_match: the "phase 2" altSubjectName to allow for this
 * connection
 *
 * Adds an allowed alternate subject name match for "phase 2".  Until
 * at least one match is added, the altSubjectName of the "phase 2"
 * remote authentication server is not verified.
 *
 * Returns: %TRUE if the "phase 2" alternative subject name match was
 *  successfully added, %FALSE if it was already allowed.
 **/
gboolean
nm_setting_802_1x_add_phase2_altsubject_match(NMSetting8021x *setting,
                                              const char     *phase2_altsubject_match)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(phase2_altsubject_match != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next(iter)) {
        if (!strcmp(phase2_altsubject_match, (char *) iter->data))
            return FALSE;
    }

    priv->phase2_altsubject_matches =
        g_slist_append(priv->phase2_altsubject_matches, g_strdup(phase2_altsubject_match));
    _notify(setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
    return TRUE;
}

/**
 * nm_setting_802_1x_remove_phase2_altsubject_match:
 * @setting: the #NMSetting8021x
 * @i: the index of the "phase 2" altSubjectName match to remove
 *
 * Removes the allowed "phase 2" altSubjectName at the specified index.
 **/
void
nm_setting_802_1x_remove_phase2_altsubject_match(NMSetting8021x *setting, guint32 i)
{
    NMSetting8021xPrivate *priv;
    GSList                *elt;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    elt  = g_slist_nth(priv->phase2_altsubject_matches, i);
    g_return_if_fail(elt != NULL);

    g_free(elt->data);
    priv->phase2_altsubject_matches = g_slist_delete_link(priv->phase2_altsubject_matches, elt);
    _notify(setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
}

/**
 * nm_setting_802_1x_remove_phase2_altsubject_match_by_value:
 * @setting: the #NMSetting8021x
 * @phase2_altsubject_match: the "phase 2" altSubjectName to remove
 *
 * Removes the allowed "phase 2" altSubjectName @phase2_altsubject_match.
 *
 * Returns: %TRUE if the alternative subject name match for "phase 2" was found and removed,
 *          %FALSE if it was not.
 **/
gboolean
nm_setting_802_1x_remove_phase2_altsubject_match_by_value(NMSetting8021x *setting,
                                                          const char     *phase2_altsubject_match)
{
    NMSetting8021xPrivate *priv;
    GSList                *iter;

    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);
    g_return_val_if_fail(phase2_altsubject_match != NULL, FALSE);

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next(iter)) {
        if (!strcmp(phase2_altsubject_match, (char *) iter->data)) {
            priv->phase2_altsubject_matches =
                g_slist_delete_link(priv->phase2_altsubject_matches, iter);
            _notify(setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
            return TRUE;
        }
    }
    return FALSE;
}

/**
 * nm_setting_802_1x_clear_phase2_altsubject_matches:
 * @setting: the #NMSetting8021x
 *
 * Clears all "phase 2" altSubjectName matches.
 **/
void
nm_setting_802_1x_clear_phase2_altsubject_matches(NMSetting8021x *setting)
{
    NMSetting8021xPrivate *priv;

    g_return_if_fail(NM_IS_SETTING_802_1X(setting));

    priv = NM_SETTING_802_1X_GET_PRIVATE(setting);
    g_slist_free_full(priv->phase2_altsubject_matches, g_free);
    priv->phase2_altsubject_matches = NULL;
    _notify(setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the "phase 2" client certificate.  If the
 * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
 * nm_setting_802_1x_get_client_cert_blob(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
 * nm_setting_802_1x_get_client_cert_path(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PKCS11, use
 * nm_setting_802_1x_get_client_cert_uri().
 *
 * Returns: scheme used to store the "phase 2" client certificate (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_phase2_client_cert_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, phase2_client_cert);
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_blob:
 * @setting: the #NMSetting8021x
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: (transfer none): the "phase 2" client certificate data
 **/
GBytes *
nm_setting_802_1x_get_phase2_client_cert_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, phase2_client_cert);
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_path:
 * @setting: the #NMSetting8021x
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: path to the "phase 2" client certificate file
 **/
const char *
nm_setting_802_1x_get_phase2_client_cert_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, phase2_client_cert);
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the "phase 2" client certificate URI analogously to
 * nm_setting_802_1x_get_phase2_ca_cert_blob() and
 * nm_setting_802_1x_get_phase2_ca_cert_path().
 *
 * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_phase2_client_cert_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, phase2_client_cert);
}

/**
 * nm_setting_802_1x_set_phase2_client_cert:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
 *   or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" client
 *   certificate file (PEM, DER, or PKCS#<!-- -->12 format).  The path must be UTF-8
 *   encoded; use g_filename_to_utf8() to convert if needed.  Passing %NULL with
 *   any @scheme clears the "phase2" client certificate.
 * @scheme: desired storage scheme for the certificate
 * @out_format: on successful return, the type of the certificate added
 * @error: on unsuccessful return, an error
 *
 * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert
 * property with the raw certificate data if using the
 * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
 * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * Client certificates are used to identify the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_phase2_client_cert(NMSetting8021x         *setting,
                                         const char             *value,
                                         NMSetting8021xCKScheme  scheme,
                                         NMSetting8021xCKFormat *out_format,
                                         GError                **error)
{
    return _cert_impl_set(setting, PROP_PHASE2_CLIENT_CERT, value, NULL, scheme, out_format, error);
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the password used to access the "phase2" client certificate stored in
 * #NMSetting8021x:phase2-client-cert property. Only makes sense if the certificate
 * is stored on a PKCS#<!-- -->11 token that requires a login.
 *
 * Since: 1.8
 **/
const char *
nm_setting_802_1x_get_phase2_client_cert_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_client_cert_password;
}

/**
 * nm_setting_802_1x_get_phase2_client_cert_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:phase2-client-cert-password
 *
 * Since: 1.8
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_phase2_client_cert_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_client_cert_password_flags;
}

/**
 * nm_setting_802_1x_get_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the password used by the authentication method, if any, as specified
 *   by the #NMSetting8021x:password property
 **/
const char *
nm_setting_802_1x_get_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->password;
}

/**
 * nm_setting_802_1x_get_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the #NMSetting8021x:password
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->password_flags;
}

/**
 * nm_setting_802_1x_get_password_raw:
 * @setting: the #NMSetting8021x
 *
 * Returns: (transfer none): the password used by the authentication method as a
 * UTF-8-encoded array of bytes, as specified by the
 * #NMSetting8021x:password-raw property
 **/
GBytes *
nm_setting_802_1x_get_password_raw(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->password_raw;
}

/**
 * nm_setting_802_1x_get_password_raw_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 *   #NMSetting8021x:password-raw
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_password_raw_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->password_raw_flags;
}

/**
 * nm_setting_802_1x_get_pin:
 * @setting: the #NMSetting8021x
 *
 * Returns: the PIN used by the authentication method, if any, as specified
 *   by the #NMSetting8021x:pin property
 **/
const char *
nm_setting_802_1x_get_pin(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->pin;
}

/**
 * nm_setting_802_1x_get_pin_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:pin
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_pin_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->pin_flags;
}

/**
 * nm_setting_802_1x_get_private_key_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the private key.  If the returned scheme is
 * %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
 * nm_setting_802_1x_get_client_cert_blob(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
 * nm_setting_802_1x_get_client_cert_path(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PKCS11, use
 * nm_setting_802_1x_get_client_cert_uri().
 *
 * Returns: scheme used to store the private key (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_private_key_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, private_key);
}

/**
 * nm_setting_802_1x_get_private_key_blob:
 * @setting: the #NMSetting8021x
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * WARNING: the private key property is not a "secret" property, and thus
 * unencrypted private key data may be readable by unprivileged users.  Private
 * keys should always be encrypted with a private key password.
 *
 * Returns: (transfer none): the private key data
 **/
GBytes *
nm_setting_802_1x_get_private_key_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, private_key);
}

/**
 * nm_setting_802_1x_get_private_key_path:
 * @setting: the #NMSetting8021x
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: path to the private key file
 **/
const char *
nm_setting_802_1x_get_private_key_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, private_key);
}

/**
 * nm_setting_802_1x_get_private_key_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the private key URI analogously to
 * nm_setting_802_1x_get_private_key_blob() and
 * nm_setting_802_1x_get_private_key_path().
 *
 * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_private_key_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, private_key);
}

/**
 * nm_setting_802_1x_set_private_key:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
 *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the private key file
 *   (PEM, DER, or PKCS#<!-- -->12 format).  The path must be UTF-8 encoded; use
 *   g_filename_to_utf8() to convert if needed.  Passing %NULL with any @scheme
 *   clears the private key.
 * @password: password used to decrypt the private key, or %NULL if the password
 *   is unknown.  If the password is given but fails to decrypt the private key,
 *   an error is returned.
 * @scheme: desired storage scheme for the private key
 * @out_format: on successful return, the type of the private key added
 * @error: on unsuccessful return, an error
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * This function reads a private key from disk and sets the
 * #NMSetting8021x:private-key property with the private key file data if using
 * the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the private
 * key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * If @password is given, this function attempts to decrypt the private key to
 * verify that @password is correct, and if it is, updates the
 * #NMSetting8021x:private-key-password property with the given @password.  If
 * the decryption is unsuccessful, %FALSE is returned, @error is set, and no
 * internal data is changed.  If no @password is given, the private key is
 * assumed to be valid, no decryption is performed, and the password may be set
 * at a later time.
 *
 * WARNING: the private key property is not a "secret" property, and thus
 * unencrypted private key data using the BLOB scheme may be readable by
 * unprivileged users.  Private keys should always be encrypted with a private
 * key password to prevent unauthorized access to unencrypted private key data.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_private_key(NMSetting8021x         *setting,
                                  const char             *value,
                                  const char             *password,
                                  NMSetting8021xCKScheme  scheme,
                                  NMSetting8021xCKFormat *out_format,
                                  GError                **error)
{
    return _cert_impl_set(setting, PROP_PRIVATE_KEY, value, password, scheme, out_format, error);
}

/**
 * nm_setting_802_1x_get_private_key_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the private key password used to decrypt the private key if
 *  previously set with nm_setting_802_1x_set_private_key(), or the
 *  #NMSetting8021x:private-key-password property.
 **/
const char *
nm_setting_802_1x_get_private_key_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->private_key_password;
}

/**
 * nm_setting_802_1x_get_private_key_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:private-key-password
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_private_key_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->private_key_password_flags;
}

/**
 * nm_setting_802_1x_get_private_key_format:
 * @setting: the #NMSetting8021x
 *
 * Returns: the data format of the private key data stored in the
 *   #NMSetting8021x:private-key property
 **/
NMSetting8021xCKFormat
nm_setting_802_1x_get_private_key_format(NMSetting8021x *setting)
{
    return _cert_impl_get_key_format(setting, private_key);
}

/**
 * nm_setting_802_1x_get_phase2_private_key_password:
 * @setting: the #NMSetting8021x
 *
 * Returns: the private key password used to decrypt the private key if
 *  previously set with nm_setting_802_1x_set_phase2_private_key() or the
 *  #NMSetting8021x:phase2-private-key-password property.
 **/
const char *
nm_setting_802_1x_get_phase2_private_key_password(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_private_key_password;
}

/**
 * nm_setting_802_1x_get_phase2_private_key_password_flags:
 * @setting: the #NMSetting8021x
 *
 * Returns: the #NMSettingSecretFlags pertaining to the
 * #NMSetting8021x:phase2-private-key-password
 **/
NMSettingSecretFlags
nm_setting_802_1x_get_phase2_private_key_password_flags(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NM_SETTING_SECRET_FLAG_NONE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->phase2_private_key_password_flags;
}

/**
 * nm_setting_802_1x_get_phase2_private_key_scheme:
 * @setting: the #NMSetting8021x
 *
 * Returns the scheme used to store the "phase 2" private key.  If the returned
 * scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
 * nm_setting_802_1x_get_client_cert_blob(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
 * nm_setting_802_1x_get_client_cert_path(); if
 * %NM_SETTING_802_1X_CK_SCHEME_PKCS11, use
 * nm_setting_802_1x_get_client_cert_uri().
 *
 * Returns: scheme used to store the "phase 2" private key (blob or path)
 **/
NMSetting8021xCKScheme
nm_setting_802_1x_get_phase2_private_key_scheme(NMSetting8021x *setting)
{
    _cert_impl_get_scheme(setting, phase2_private_key);
}

/**
 * nm_setting_802_1x_get_phase2_private_key_blob:
 * @setting: the #NMSetting8021x
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * WARNING: the phase2 private key property is not a "secret" property, and thus
 * unencrypted private key data may be readable by unprivileged users.  Private
 * keys should always be encrypted with a private key password.
 *
 * Returns: (transfer none): the "phase 2" private key data
 **/
GBytes *
nm_setting_802_1x_get_phase2_private_key_blob(NMSetting8021x *setting)
{
    _cert_impl_get_blob(setting, phase2_private_key);
}

/**
 * nm_setting_802_1x_get_phase2_private_key_path:
 * @setting: the #NMSetting8021x
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * Returns: path to the "phase 2" private key file
 **/
const char *
nm_setting_802_1x_get_phase2_private_key_path(NMSetting8021x *setting)
{
    _cert_impl_get_path(setting, phase2_private_key);
}

/**
 * nm_setting_802_1x_get_phase2_private_key_uri:
 * @setting: the #NMSetting8021x
 *
 * Returns the "phase 2" private key URI analogously to
 * nm_setting_802_1x_get_phase2_private_key_blob() and
 * nm_setting_802_1x_get_phase2_private_key_path().
 *
 * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
 * 7512), but may be extended to other schemes in future (such as 'file' URIs
 * for local files and 'data' URIs for inline certificate data).
 *
 * Returns: the URI string
 *
 * Since: 1.6
 **/
const char *
nm_setting_802_1x_get_phase2_private_key_uri(NMSetting8021x *setting)
{
    _cert_impl_get_uri(setting, phase2_private_key);
}

/**
 * nm_setting_802_1x_set_phase2_private_key:
 * @setting: the #NMSetting8021x
 * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
 *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" private
 *   key file (PEM, DER, or PKCS#<!-- -->12 format).  The path must be UTF-8 encoded;
 *   use g_filename_to_utf8() to convert if needed.  Passing %NULL with any
 *   @scheme clears the private key.
 * @password: password used to decrypt the private key, or %NULL if the password
 *   is unknown.  If the password is given but fails to decrypt the private key,
 *   an error is returned.
 * @scheme: desired storage scheme for the private key
 * @out_format: on successful return, the type of the private key added
 * @error: on unsuccessful return, an error
 *
 * Private keys are used to authenticate the connecting client to the network
 * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
 * authentication method.
 *
 * This function reads a private key from disk and sets the
 * #NMSetting8021x:phase2-private-key property with the private key file data if
 * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the
 * private key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
 *
 * If @password is given, this function attempts to decrypt the private key to
 * verify that @password is correct, and if it is, updates the
 * #NMSetting8021x:phase2-private-key-password property with the given
 * @password.  If the decryption is unsuccessful, %FALSE is returned, @error is
 * set, and no internal data is changed.  If no @password is given, the private
 * key is assumed to be valid, no decryption is performed, and the password may
 * be set at a later time.
 *
 * WARNING: the "phase2" private key property is not a "secret" property, and
 * thus unencrypted private key data using the BLOB scheme may be readable by
 * unprivileged users.  Private keys should always be encrypted with a private
 * key password to prevent unauthorized access to unencrypted private key data.
 *
 * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
 **/
gboolean
nm_setting_802_1x_set_phase2_private_key(NMSetting8021x         *setting,
                                         const char             *value,
                                         const char             *password,
                                         NMSetting8021xCKScheme  scheme,
                                         NMSetting8021xCKFormat *out_format,
                                         GError                **error)
{
    return _cert_impl_set(setting,
                          PROP_PHASE2_PRIVATE_KEY,
                          value,
                          password,
                          scheme,
                          out_format,
                          error);
}

/**
 * nm_setting_802_1x_get_phase2_private_key_format:
 * @setting: the #NMSetting8021x
 *
 * Returns: the data format of the "phase 2" private key data stored in the
 *   #NMSetting8021x:phase2-private-key property
 **/
NMSetting8021xCKFormat
nm_setting_802_1x_get_phase2_private_key_format(NMSetting8021x *setting)
{
    return _cert_impl_get_key_format(setting, phase2_private_key);
}

/**
 * nm_setting_802_1x_get_auth_timeout:
 * @setting: the #NMSetting8021x
 *
 * Returns the value contained in the #NMSetting8021x:auth-timeout property.
 *
 * Returns: the configured authentication timeout in seconds. Zero means the
 * global default value.
 *
 * Since: 1.8
 **/
int
nm_setting_802_1x_get_auth_timeout(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), 0);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->auth_timeout;
}

/**
 * nm_setting_802_1x_get_optional:
 * @setting: the #NMSetting8021x
 *
 * Returns the value contained in the #NMSetting8021x:optional property.
 *
 * Returns: %TRUE if the activation should proceed even when the 802.1X
 *     authentication fails; %FALSE otherwise
 *
 * Since: 1.22
 **/
gboolean
nm_setting_802_1x_get_optional(NMSetting8021x *setting)
{
    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), FALSE);

    return NM_SETTING_802_1X_GET_PRIVATE(setting)->optional;
}

/*****************************************************************************/

static void
need_secrets_password(NMSetting8021x *self,
                      GPtrArray      *secrets,
                      gboolean        phase2,
                      gboolean        check_rerequest)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);

    if (check_rerequest
        || (nm_str_is_empty(priv->password)
            && (!priv->password_raw || !g_bytes_get_size(priv->password_raw)))) {
        g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD);
        g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD_RAW);
    }
}

static void
need_secrets_sim(NMSetting8021x *self,
                 GPtrArray      *secrets,
                 gboolean        phase2,
                 gboolean        check_rerequest)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);

    if (check_rerequest || nm_str_is_empty(priv->pin))
        g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN);
}

static void
need_secrets_tls(NMSetting8021x *self,
                 GPtrArray      *secrets,
                 gboolean        phase2,
                 gboolean        check_rerequest)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
    NMSetting8021xCKScheme scheme;

    /* If check_rerequest is TRUE do not return secrets, unless missing.
     * This secret cannot be wrong. */

    if (!NM_FLAGS_HAS(phase2 ? priv->phase2_private_key_password_flags
                             : priv->private_key_password_flags,
                      NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
        NMCryptoFileFormat format       = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
        gboolean           has_password = FALSE;
        const char        *password;

        password = phase2 ? priv->phase2_private_key_password : priv->private_key_password;

        /* Check whether the password works. */
        if (password) {
            scheme = phase2 ? nm_setting_802_1x_get_phase2_private_key_scheme(self)
                            : nm_setting_802_1x_get_private_key_scheme(self);

            if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
                const char *path = phase2 ? nm_setting_802_1x_get_phase2_private_key_path(self)
                                          : nm_setting_802_1x_get_private_key_path(self);

                if (path)
                    format = nm_crypto_verify_private_key(path, password, NULL, NULL);
            } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
                GBytes *blob = phase2 ? nm_setting_802_1x_get_phase2_private_key_blob(self)
                                      : nm_setting_802_1x_get_private_key_blob(self);

                if (blob)
                    format = nm_crypto_verify_private_key_data(g_bytes_get_data(blob, NULL),
                                                               g_bytes_get_size(blob),
                                                               password,
                                                               NULL,
                                                               NULL);
            } else {
                /* For PKCS#11 URLS, we assume the password is correct. */
                has_password = TRUE;
            }
        }
        if (!has_password && format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
            g_ptr_array_add(secrets,
                            phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD
                                   : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
        }
    }

    if (!NM_FLAGS_HAS(phase2 ? priv->phase2_ca_cert_password_flags : priv->ca_cert_password_flags,
                      NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
        scheme = phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(self)
                        : nm_setting_802_1x_get_ca_cert_scheme(self);
        if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
            && !(phase2 ? priv->phase2_ca_cert_password : priv->ca_cert_password)) {
            g_ptr_array_add(secrets,
                            phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD
                                   : NM_SETTING_802_1X_CA_CERT_PASSWORD);
        }
    }

    if (!NM_FLAGS_HAS(phase2 ? priv->phase2_client_cert_password_flags
                             : priv->client_cert_password_flags,
                      NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
        scheme = phase2 ? nm_setting_802_1x_get_phase2_client_cert_scheme(self)
                        : nm_setting_802_1x_get_client_cert_scheme(self);
        if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
            && !(phase2 ? priv->phase2_client_cert_password : priv->client_cert_password)) {
            g_ptr_array_add(secrets,
                            phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD
                                   : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
        }
    }
}

static gboolean
verify_identity(NMSetting8021x *self, gboolean phase2, GError **error)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);

    if (nm_str_is_empty(priv->identity)) {
        if (!priv->identity) {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_MISSING_PROPERTY,
                                _("property is missing"));
        } else {
            g_set_error_literal(error,
                                NM_CONNECTION_ERROR,
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
                                _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_IDENTITY);
        return FALSE;
    }

    return TRUE;
}

static gboolean
verify_tls(NMSetting8021x *self, gboolean phase2, GError **error)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
    GBytes                *client_cert;
    GBytes                *private_key;
    const char            *prop_client_cert;
    const char            *prop_private_key;

    client_cert = phase2 ? priv->phase2_client_cert : priv->client_cert;
    private_key = phase2 ? priv->phase2_private_key : priv->private_key;
    prop_client_cert =
        phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT;
    prop_private_key =
        phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY;

    if (!client_cert) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
                            _("property is missing"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert);
        return FALSE;
    }
    if (g_bytes_get_size(client_cert) == 0) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("property is empty"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert);
        return FALSE;
    }

    /* Private key is required for TLS */
    if (!private_key) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
                            _("property is missing"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key);
        return FALSE;
    }

    if (g_bytes_get_size(private_key) == 0) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("property is empty"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key);
        return FALSE;
    }

    if (_nm_setting_802_1x_cert_get_scheme(private_key, NULL) == NM_SETTING_802_1X_CK_SCHEME_BLOB
        && nm_crypto_is_pkcs12_data(g_bytes_get_data(private_key, NULL),
                                    g_bytes_get_size(private_key),
                                    NULL)) {
        /* If the private key is PKCS#12, check that it matches the client cert */
        if (!g_bytes_equal(private_key, client_cert)) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("has to match '%s' property for PKCS#12"),
                        prop_private_key);
            g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert);
            return FALSE;
        }
    }

    return TRUE;
}

static gboolean
verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error)
{
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);

    if (!verify_identity(self, phase2, error))
        return FALSE;

    if ((!priv->phase2_auth && !priv->phase2_autheap)
        || (priv->phase2_auth && priv->phase2_autheap)) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
                            _("exactly one property must be set"));
        g_prefix_error(error,
                       "%s.%s, %s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE2_AUTH,
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE2_AUTHEAP);
        return FALSE;
    }

    return TRUE;
}

static void
need_secrets_phase2(NMSetting8021x *self,
                    GPtrArray      *secrets,
                    gboolean        phase2,
                    gboolean        check_rerequest)
{
    NMSetting8021xPrivate *priv   = NM_SETTING_802_1X_GET_PRIVATE(self);
    char                  *method = NULL;
    int                    i;

    g_return_if_fail(phase2 == FALSE);

    /* Check phase2_auth and phase2_autheap */
    method = priv->phase2_auth;
    if (!method)
        method = priv->phase2_autheap;

    if (!method)
        g_return_if_reached();

    /* Ask the configured phase2 method if it needs secrets */
    for (i = 0; eap_methods_table[i].method; i++) {
        if (!eap_methods_table[i].ns_func)
            continue;
        if (nm_streq(eap_methods_table[i].method, method)) {
            (*eap_methods_table[i].ns_func)(self, secrets, TRUE, check_rerequest);
            break;
        }
    }
}

static const EAPMethodsTable eap_methods_table[] = {
    {"leap", need_secrets_password, verify_identity},
    {"pwd", need_secrets_password, verify_identity},
    {"md5", need_secrets_password, verify_identity},
    {"pap", need_secrets_password, verify_identity},
    {"chap", need_secrets_password, verify_identity},
    {"mschap", need_secrets_password, verify_identity},
    {"mschapv2", need_secrets_password, verify_identity},
    {"fast", need_secrets_password, verify_identity},
    {"tls", need_secrets_tls, verify_tls},
    {"peap", need_secrets_phase2, verify_ttls},
    {"ttls", need_secrets_phase2, verify_ttls},
    {"sim", need_secrets_sim, NULL},
    {"gtc", need_secrets_password, verify_identity},
    {"otp", NULL, NULL},  // FIXME: implement
    {"external", NULL, NULL},
    {NULL, NULL, NULL}};

static gboolean
verify(NMSetting *setting, NMConnection *connection, GError **error)
{
    NMSetting8021x        *self = NM_SETTING_802_1X(setting);
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
    const char            *valid_eap[] =
        {"leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", "external", NULL};
    GSList *iter;

    if (error)
        g_return_val_if_fail(*error == NULL, FALSE);

    if (connection && priv->optional
        && !nm_streq0(nm_connection_get_connection_type(connection),
                      NM_SETTING_WIRED_SETTING_NAME)) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("can be enabled only on Ethernet connections"));
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_OPTIONAL);
        return FALSE;
    }

    if (!priv->eap) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
                            _("property is missing"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP);
        return FALSE;
    }

    if (!_nm_utils_string_slist_validate(priv->eap, valid_eap)) {
        g_set_error_literal(error,
                            NM_CONNECTION_ERROR,
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
                            _("property is invalid"));
        g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP);
        return FALSE;
    }

    /* Ask each configured EAP method if its valid */
    for (iter = priv->eap; iter; iter = g_slist_next(iter)) {
        const char *method = (const char *) iter->data;
        int         i;

        for (i = 0; eap_methods_table[i].method; i++) {
            if (eap_methods_table[i].v_func == NULL)
                continue;
            if (!strcmp(eap_methods_table[i].method, method)) {
                if (!(*eap_methods_table[i].v_func)(self, FALSE, error))
                    return FALSE;
                break;
            }
        }
    }

    if (!NM_IN_STRSET(priv->phase1_peapver, NULL, "0", "1")) {
        if (priv->phase1_peapver) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("'%s' is not a valid value for the property"),
                        priv->phase1_peapver);
        } else {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE1_PEAPVER);
        return FALSE;
    }

    if (!NM_IN_STRSET(priv->phase1_peaplabel, NULL, "0", "1")) {
        if (priv->phase1_peaplabel) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("'%s' is not a valid value for the property"),
                        priv->phase1_peaplabel);
        } else {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE1_PEAPLABEL);
        return FALSE;
    }

    if (!NM_IN_STRSET(priv->phase1_fast_provisioning, NULL, "0", "1", "2", "3")) {
        if (priv->phase1_fast_provisioning) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("'%s' is not a valid value for the property"),
                        priv->phase1_fast_provisioning);
        } else {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING);
        return FALSE;
    }

    if (NM_FLAGS_ANY(priv->phase1_auth_flags, ~((guint32) NM_SETTING_802_1X_AUTH_FLAGS_ALL))) {
        g_set_error(error,
                    NM_CONNECTION_ERROR,
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
                    _("invalid auth flags: '%d' contains unknown flags"),
                    priv->phase1_auth_flags);
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE1_AUTH_FLAGS);
        return FALSE;
    }

    if (NM_FLAGS_ALL(priv->phase1_auth_flags,
                     NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE
                         | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE)
        || NM_FLAGS_ALL(priv->phase1_auth_flags,
                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE
                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE)
        || NM_FLAGS_ALL(priv->phase1_auth_flags,
                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE
                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE)
        || NM_FLAGS_ALL(priv->phase1_auth_flags,
                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE
                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE)) {
        g_set_error_literal(
            error,
            NM_CONNECTION_ERROR,
            NM_CONNECTION_ERROR_INVALID_PROPERTY,
            _("invalid auth flags: both enable and disable are set for the same TLS version"));
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE1_AUTH_FLAGS);
        return FALSE;
    }

    if (!NM_IN_STRSET(priv->phase2_auth,
                      NULL,
                      "pap",
                      "chap",
                      "mschap",
                      "mschapv2",
                      "gtc",
                      "otp",
                      "md5",
                      "tls")) {
        if (priv->phase2_auth) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("'%s' is not a valid value for the property"),
                        priv->phase2_auth);
        } else {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE2_AUTH);
        return FALSE;
    }

    if (!NM_IN_STRSET(priv->phase2_autheap, NULL, "md5", "mschapv2", "otp", "gtc", "tls")) {
        if (priv->phase2_autheap) {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("'%s' is not a valid value for the property"),
                        priv->phase2_autheap);
        } else {
            g_set_error(error,
                        NM_CONNECTION_ERROR,
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
                        _("property is empty"));
        }
        g_prefix_error(error,
                       "%s.%s: ",
                       NM_SETTING_802_1X_SETTING_NAME,
                       NM_SETTING_802_1X_PHASE2_AUTHEAP);
        return FALSE;
    }

    if (!_cert_verify_property(priv->ca_cert,
                               NM_SETTING_802_1X_CA_CERT,
                               priv->ca_cert_password,
                               NM_SETTING_802_1X_CA_CERT_PASSWORD,
                               error))
        return FALSE;
    if (!_cert_verify_property(priv->phase2_ca_cert,
                               NM_SETTING_802_1X_PHASE2_CA_CERT,
                               priv->phase2_ca_cert_password,
                               NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
                               error))
        return FALSE;

    if (!_cert_verify_property(priv->client_cert,
                               NM_SETTING_802_1X_CLIENT_CERT,
                               priv->client_cert_password,
                               NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
                               error))
        return FALSE;
    if (!_cert_verify_property(priv->phase2_client_cert,
                               NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
                               priv->phase2_client_cert_password,
                               NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
                               error))
        return FALSE;

    if (!_cert_verify_property(priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, NULL, NULL, error))
        return FALSE;
    if (!_cert_verify_property(priv->phase2_private_key,
                               NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
                               NULL,
                               NULL,
                               error))
        return FALSE;

#define _check_strempty_and_return(priv, prop_name, field, error)                               \
    G_STMT_START                                                                                \
    {                                                                                           \
        NMSetting8021xPrivate *_priv  = (priv);                                                 \
        GError               **_error = (error);                                                \
                                                                                                \
        if (_priv->field && _priv->field[0] == '\0') {                                          \
            g_set_error(_error,                                                                 \
                        NM_CONNECTION_ERROR,                                                    \
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,                                   \
                        _("property is empty"));                                                \
            g_prefix_error(_error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, "" prop_name ""); \
            return NM_SETTING_VERIFY_NORMALIZABLE;                                              \
        }                                                                                       \
    }                                                                                           \
    G_STMT_END

    /* normalizable warnings from here on. */

    _check_strempty_and_return(priv, NM_SETTING_802_1X_IDENTITY, identity, error);
    _check_strempty_and_return(priv,
                               NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
                               anonymous_identity,
                               error);
    _check_strempty_and_return(priv, NM_SETTING_802_1X_PAC_FILE, pac_file, error);
    _check_strempty_and_return(priv, NM_SETTING_802_1X_SUBJECT_MATCH, subject_match, error);
    _check_strempty_and_return(priv,
                               NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH,
                               phase2_subject_match,
                               error);
    _check_strempty_and_return(priv,
                               NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH,
                               domain_suffix_match,
                               error);
    _check_strempty_and_return(priv,
                               NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH,
                               phase2_domain_suffix_match,
                               error);
    _check_strempty_and_return(priv, NM_SETTING_802_1X_DOMAIN_MATCH, domain_match, error);
    _check_strempty_and_return(priv,
                               NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH,
                               phase2_domain_match,
                               error);

    return TRUE;
}

/*****************************************************************************/

static GPtrArray *
need_secrets(NMSetting *setting, gboolean check_rerequest)
{
    NMSetting8021x        *self = NM_SETTING_802_1X(setting);
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
    GSList                *iter;
    GPtrArray             *secrets;
    gboolean               eap_method_found = FALSE;

    secrets = g_ptr_array_sized_new(4);

    /* Ask each configured EAP method if it needs secrets */
    for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next(iter)) {
        const char *method = (const char *) iter->data;
        int         i;

        for (i = 0; eap_methods_table[i].method; i++) {
            if (eap_methods_table[i].ns_func == NULL)
                continue;
            if (!strcmp(eap_methods_table[i].method, method)) {
                (*eap_methods_table[i].ns_func)(self, secrets, FALSE, check_rerequest);

                /* Only break out of the outer loop if this EAP method
                 * needed secrets.
                 */
                if (secrets->len > 0)
                    eap_method_found = TRUE;
                break;
            }
        }
    }

    if (secrets->len == 0) {
        g_ptr_array_free(secrets, TRUE);
        return NULL;
    }

    return secrets;
}

/*****************************************************************************/

static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
    NMSetting8021x        *setting = NM_SETTING_802_1X(object);
    NMSetting8021xPrivate *priv    = NM_SETTING_802_1X_GET_PRIVATE(setting);

    switch (prop_id) {
    case PROP_EAP:
        g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->eap, TRUE));
        break;
    case PROP_ALTSUBJECT_MATCHES:
        g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->altsubject_matches, TRUE));
        break;
    case PROP_PHASE2_ALTSUBJECT_MATCHES:
        g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->phase2_altsubject_matches, TRUE));
        break;
    default:
        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
        break;
    }
}

static void
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
    NMSetting8021x        *setting = NM_SETTING_802_1X(object);
    NMSetting8021xPrivate *priv    = NM_SETTING_802_1X_GET_PRIVATE(setting);

    switch (prop_id) {
    case PROP_EAP:
        g_slist_free_full(priv->eap, g_free);
        priv->eap = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
        break;
    case PROP_ALTSUBJECT_MATCHES:
        g_slist_free_full(priv->altsubject_matches, g_free);
        priv->altsubject_matches = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
        break;
    case PROP_PHASE2_ALTSUBJECT_MATCHES:
        g_slist_free_full(priv->phase2_altsubject_matches, g_free);
        priv->phase2_altsubject_matches = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
        break;
    default:
        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
        break;
    }
}

/*****************************************************************************/

static void
nm_setting_802_1x_init(NMSetting8021x *setting)
{}

/**
 * nm_setting_802_1x_new:
 *
 * Creates a new #NMSetting8021x object with default values.
 *
 * Returns: the new empty #NMSetting8021x object
 **/
NMSetting *
nm_setting_802_1x_new(void)
{
    return g_object_new(NM_TYPE_SETTING_802_1X, NULL);
}

static void
finalize(GObject *object)
{
    NMSetting8021x        *self = NM_SETTING_802_1X(object);
    NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);

    g_slist_free_full(priv->eap, g_free);
    g_slist_free_full(priv->altsubject_matches, g_free);
    g_slist_free_full(priv->phase2_altsubject_matches, g_free);

    G_OBJECT_CLASS(nm_setting_802_1x_parent_class)->finalize(object);
}

static void
nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
{
    GObjectClass   *object_class        = G_OBJECT_CLASS(klass);
    NMSettingClass *setting_class       = NM_SETTING_CLASS(klass);
    GArray         *properties_override = _nm_sett_info_property_override_create_array_sized(55);

    object_class->get_property = get_property;
    object_class->set_property = set_property;
    object_class->finalize     = finalize;

    setting_class->verify       = verify;
    setting_class->need_secrets = need_secrets;

    /**
     * NMSetting8021x:eap:
     *
     * The allowed EAP method to be used when authenticating to the network with
     * 802.1x.  Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd",
     * and "fast".  Each method requires different configuration using the
     * properties of this setting; refer to wpa_supplicant documentation for the
     * allowed combinations.
     **/
    /* ---ifcfg-rh---
     * property: eap
     * variable: IEEE_8021X_EAP_METHODS(+)
     * values: "LEAP", "PWD", "TLS", "PEAP", "TTLS", "FAST"
     * description: EAP method for 802.1X authentication.
     * example: IEEE_8021X_EAP_METHODS=PEAP
     * ---end---
     */
    _nm_setting_property_define_gprop_strv_oldstyle(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_EAP,
                                                    PROP_EAP,
                                                    NM_SETTING_PARAM_NONE);

    /**
     * NMSetting8021x:identity:
     *
     * Identity string for EAP authentication methods.  Often the user's user or
     * login name.
     **/
    /* ---ifcfg-rh---
     * property: identity
     * variable: IEEE_8021X_IDENTITY(+)
     * description: Identity for EAP authentication methods.
     * example: IEEE_8021X_IDENTITY=itsme
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_IDENTITY,
                                              PROP_IDENTITY,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              identity,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:anonymous-identity:
     *
     * Anonymous identity string for EAP authentication methods.  Used as the
     * unencrypted identity with EAP types that support different tunneled
     * identity like EAP-TTLS.
     **/
    /* ---ifcfg-rh---
     * property: anonymous-identity
     * variable: IEEE_8021X_ANON_IDENTITY(+)
     * description: Anonymous identity for EAP authentication methods.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
                                              PROP_ANONYMOUS_IDENTITY,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              anonymous_identity,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:pac-file:
     *
     * UTF-8 encoded file path containing PAC for EAP-FAST.
     **/
    /* ---ifcfg-rh---
     * property: pac-file
     * variable: IEEE_8021X_PAC_FILE(+)
     * description: File with PAC (Protected Access Credential) for EAP-FAST.
     * example: IEEE_8021X_PAC_FILE=/home/joe/my-fast.pac
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PAC_FILE,
                                              PROP_PAC_FILE,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              pac_file,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:ca-cert:
     *
     * Contains the CA certificate if used by the EAP method specified in the
     * #NMSetting8021x:eap property.
     *
     * Certificate data is specified using a "scheme"; three are currently
     * supported: blob, path and pkcs#11 URL. When using the blob scheme this property
     * should be set to the certificate's DER encoded data. When using the path
     * scheme, this property should be set to the full UTF-8 encoded path of the
     * certificate, prefixed with the string "file://" and ending with a terminating
     * NUL byte.
     * This property can be unset even if the EAP method supports CA certificates,
     * but this allows man-in-the-middle attacks and is NOT recommended.
     *
     * Note that enabling NMSetting8021x:system-ca-certs will override this
     * setting to use the built-in path, if the built-in path is not a directory.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_ca_cert() function instead.
     **/
    /* ---ifcfg-rh---
     * property: ca-cert
     * variable: IEEE_8021X_CA_CERT(+)
     * description: CA certificate for EAP.
     * example: IEEE_8021X_CA_CERT=/home/joe/cacert.crt
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_CA_CERT,
                                             PROP_CA_CERT,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             ca_cert);

    /**
     * NMSetting8021x:ca-cert-password:
     *
     * The password used to access the CA certificate stored in
     * #NMSetting8021x:ca-cert property. Only makes sense if the certificate
     * is stored on a PKCS#<!-- -->11 token that requires a login.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_CA_CERT_PASSWORD,
                                              PROP_CA_CERT_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              ca_cert_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:ca-cert-password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:ca-cert-password property.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS,
                                                    PROP_CA_CERT_PASSWORD_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    ca_cert_password_flags);

    /**
     * NMSetting8021x:ca-path:
     *
     * UTF-8 encoded path to a directory containing PEM or DER formatted
     * certificates to be added to the verification chain in addition to the
     * certificate specified in the #NMSetting8021x:ca-cert property.
     *
     * If NMSetting8021x:system-ca-certs is enabled and the built-in CA
     * path is an existing directory, then this setting is ignored.
     **/
    /* ---ifcfg-rh---
     * property: ca-path
     * variable: IEEE_8021X_CA_PATH(+)
     * description: The search path for the certificate.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_CA_PATH,
                                              PROP_CA_PATH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              ca_path,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:subject-match:
     *
     * Substring to be matched against the subject of the certificate presented
     * by the authentication server. When unset, no verification of the
     * authentication server certificate's subject is performed. This property
     * provides little security, if any, and should not be used.
     *
     * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead.
     **/
    /* ---ifcfg-rh---
     * property: subject-match
     * variable: IEEE_8021X_SUBJECT_MATCH(+)
     * description: Substring to match subject of server certificate against.
     * example: IEEE_8021X_SUBJECT_MATCH="Red Hat"
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_SUBJECT_MATCH,
                                              PROP_SUBJECT_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              subject_match,
                                              .is_deprecated             = TRUE,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:altsubject-matches:
     *
     * List of strings to be matched against the altSubjectName of the
     * certificate presented by the authentication server. If the list is empty,
     * no verification of the server certificate's altSubjectName is performed.
     **/
    /* ---ifcfg-rh---
     * property: altsubject-matches
     * variable: IEEE_8021X_ALTSUBJECT_MATCHES(+)
     * description: List of strings to be matched against the altSubjectName.
     * example: IEEE_8021X_ALTSUBJECT_MATCHES="s1.domain.cc"
     * ---end---
     */
    _nm_setting_property_define_gprop_strv_oldstyle(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_ALTSUBJECT_MATCHES,
                                                    PROP_ALTSUBJECT_MATCHES,
                                                    NM_SETTING_PARAM_NONE);

    /**
     * NMSetting8021x:domain-suffix-match:
     *
     * Constraint for server domain name. If set, this FQDN is used as a suffix
     * match requirement for dNSName element(s) of the certificate presented by
     * the authentication server.  If a matching dNSName is found, this
     * constraint is met.  If no dNSName values are present, this constraint is
     * matched against SubjectName CN using same suffix match comparison.
     * Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited
     * list.
     *
     * Since: 1.2
     **/
    /* ---ifcfg-rh---
     * property: domain-suffix-match
     * description: Suffix to match domain of server certificate against.
     * variable: IEEE_8021X_DOMAIN_SUFFIX_MATCH(+)
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH,
                                              PROP_DOMAIN_SUFFIX_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              domain_suffix_match,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:domain-match:
     *
     * Constraint for server domain name. If set, this list of FQDNs is used as
     * a match requirement for dNSName element(s) of the certificate presented
     * by the authentication server.  If a matching dNSName is found, this
     * constraint is met.  If no dNSName values are present, this constraint is
     * matched against SubjectName CN using the same comparison.
     * Multiple valid FQDNs can be passed as a ";" delimited list.
     *
     * Since: 1.24
     **/
    /* ---ifcfg-rh---
     * property: domain-match
     * description: Value to match domain of server certificate against.
     * variable: IEEE_8021X_DOMAIN_MATCH(+)
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_DOMAIN_MATCH,
                                              PROP_DOMAIN_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              domain_match,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:client-cert:
     *
     * Contains the client certificate if used by the EAP method specified in
     * the #NMSetting8021x:eap property.
     *
     * Certificate data is specified using a "scheme"; two are currently
     * supported: blob and path. When using the blob scheme (which is backwards
     * compatible with NM 0.7.x) this property should be set to the
     * certificate's DER encoded data. When using the path scheme, this property
     * should be set to the full UTF-8 encoded path of the certificate, prefixed
     * with the string "file://" and ending with a terminating NUL byte.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_client_cert() function instead.
     **/
    /* ---ifcfg-rh---
     * property: client-cert
     * variable: IEEE_8021X_CLIENT_CERT(+)
     * description: Client certificate for EAP.
     * example: IEEE_8021X_CLIENT_CERT=/home/joe/mycert.crt
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_CLIENT_CERT,
                                             PROP_CLIENT_CERT,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             client_cert);

    /**
     * NMSetting8021x:client-cert-password:
     *
     * The password used to access the client certificate stored in
     * #NMSetting8021x:client-cert property. Only makes sense if the certificate
     * is stored on a PKCS#<!-- -->11 token that requires a login.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
                                              PROP_CLIENT_CERT_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              client_cert_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:client-cert-password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:client-cert-password property.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS,
                                                    PROP_CLIENT_CERT_PASSWORD_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    client_cert_password_flags);

    /**
     * NMSetting8021x:phase1-peapver:
     *
     * Forces which PEAP version is used when PEAP is set as the EAP method in
     * the #NMSetting8021x:eap property.  When unset, the version reported by
     * the server will be used.  Sometimes when using older RADIUS servers, it
     * is necessary to force the client to use a particular PEAP version.  To do
     * so, this property may be set to "0" or "1" to force that specific PEAP
     * version.
     **/
    /* ---ifcfg-rh---
     * property: phase1-peapver
     * variable: IEEE_8021X_PEAP_VERSION(+)
     * values: 0, 1
     * description: Use to force a specific PEAP version.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE1_PEAPVER,
                                              PROP_PHASE1_PEAPVER,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase1_peapver,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase1-peaplabel:
     *
     * Forces use of the new PEAP label during key derivation.  Some RADIUS
     * servers may require forcing the new PEAP label to interoperate with
     * PEAPv1.  Set to "1" to force use of the new PEAP label.  See the
     * wpa_supplicant documentation for more details.
     **/
    /* ---ifcfg-rh---
     * property: phase1-peaplabel
     * variable: IEEE_8021X_PEAP_FORCE_NEW_LABEL(+)
     * values: yes, no
     * default: no
     * description: Use to force the new PEAP label during key derivation.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE1_PEAPLABEL,
                                              PROP_PHASE1_PEAPLABEL,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase1_peaplabel,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase1-fast-provisioning:
     *
     * Enables or disables in-line provisioning of EAP-FAST credentials when
     * FAST is specified as the EAP method in the #NMSetting8021x:eap property.
     * Recognized values are "0" (disabled), "1" (allow unauthenticated
     * provisioning), "2" (allow authenticated provisioning), and "3" (allow
     * both authenticated and unauthenticated provisioning).  See the
     * wpa_supplicant documentation for more details.
     **/
    /* ---ifcfg-rh---
     * property: phase1-fast-provisioning
     * variable: IEEE_8021X_FAST_PROVISIONING(+)
     * values: space-separated list of these values [allow-auth, allow-unauth]
     * description: Enable in-line provisioning of EAP-FAST credentials.
     * example: IEEE_8021X_FAST_PROVISIONING="allow-auth allow-unauth"
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
                                              PROP_PHASE1_FAST_PROVISIONING,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase1_fast_provisioning,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase1-auth-flags:
     *
     * Specifies authentication flags to use in "phase 1" outer
     * authentication using #NMSetting8021xAuthFlags options.
     * The individual TLS versions can be explicitly disabled. TLS time checks
     * can be also disabled. If a certain TLS disable flag is not
     * set, it is up to the supplicant to allow or forbid it. The TLS options
     * map to tls_disable_tlsv1_x and tls_disable_time_checks settings.
     * See the wpa_supplicant documentation for more details.
     *
     * Since: 1.8
     */
    /* ---ifcfg-rh---
     * property: phase1-auth-flags
     * variable: IEEE_8021X_PHASE1_AUTH_FLAGS(+)
     * values: space-separated list of authentication flags names
     * description: Authentication flags for the supplicant
     * example: IEEE_8021X_PHASE1_AUTH_FLAGS="tls-1-0-disable tls-1-1-disable"
     * ---end---
     */
    _nm_setting_property_define_direct_uint32(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
                                              PROP_PHASE1_AUTH_FLAGS,
                                              0,
                                              G_MAXUINT32,
                                              NM_SETTING_802_1X_AUTH_FLAGS_NONE,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase1_auth_flags);

    /**
     * NMSetting8021x:phase2-auth:
     *
     * Specifies the allowed "phase 2" inner authentication method when an EAP
     * method that uses an inner TLS tunnel is specified in the #NMSetting8021x:eap
     * property.  For TTLS this property selects one of the supported non-EAP
     * inner methods: "pap", "chap", "mschap", "mschapv2" while
     * #NMSetting8021x:phase2-autheap selects an EAP inner method.  For PEAP
     * this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls".
     * Each "phase 2" inner method requires specific parameters for successful
     * authentication; see the wpa_supplicant documentation for more details.
     * Both #NMSetting8021x:phase2-auth and #NMSetting8021x:phase2-autheap cannot
     * be specified.
     **/
    /* ---ifcfg-rh---
     * property: phase2-auth
     * variable: IEEE_8021X_INNER_AUTH_METHODS(+)
     * values: "PAP", "CHAP", "MSCHAP", "MSCHAPV2", "GTC", "OTP", "MD5" and "TLS"
     * description: Inner non-EAP authentication methods for TTLS or the inner EAP
     *   authentication method for PEAP. IEEE_8021X_INNER_AUTH_METHODS can contain
     *   values both for 'phase2-auth' and 'phase2-autheap' properties.
     * example: IEEE_8021X_INNER_AUTH_METHODS=PAP
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_AUTH,
                                              PROP_PHASE2_AUTH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_auth,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-autheap:
     *
     * Specifies the allowed "phase 2" inner EAP-based authentication method
     * when TTLS is specified in the #NMSetting8021x:eap property.  Recognized
     * EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and
     * "tls". Each "phase 2" inner method requires specific parameters for
     * successful authentication; see the wpa_supplicant documentation for
     * more details.
     **/
    /* ---ifcfg-rh---
     * property: phase2-autheap
     * variable: IEEE_8021X_INNER_AUTH_METHODS(+)
     * values: "EAP-MD5", "EAP-MSCHAPV2", "EAP-GTC", "EAP-OTP" and "EAP-TLS"
     * description: Inner EAP-based authentication methods. Note that
     *   IEEE_8021X_INNER_AUTH_METHODS is also used for 'phase2-auth' values.
     * example: IEEE_8021X_INNER_AUTH_METHODS="MSCHAPV2 EAP-TLS"
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_AUTHEAP,
                                              PROP_PHASE2_AUTHEAP,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_autheap,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-ca-cert:
     *
     * Contains the "phase 2" CA certificate if used by the EAP method specified
     * in the #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap
     * properties.
     *
     * Certificate data is specified using a "scheme"; three are currently
     * supported: blob, path and pkcs#11 URL. When using the blob scheme this property
     * should be set to the certificate's DER encoded data. When using the path
     * scheme, this property should be set to the full UTF-8 encoded path of the
     * certificate, prefixed with the string "file://" and ending with a terminating
     * NUL byte.
     * This property can be unset even if the EAP method supports CA certificates,
     * but this allows man-in-the-middle attacks and is NOT recommended.
     *
     * Note that enabling NMSetting8021x:system-ca-certs will override this
     * setting to use the built-in path, if the built-in path is not a directory.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_phase2_ca_cert() function instead.
     **/
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_PHASE2_CA_CERT,
                                             PROP_PHASE2_CA_CERT,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             phase2_ca_cert);

    /**
     * NMSetting8021x:phase2-ca-cert-password:
     *
     * The password used to access the "phase2" CA certificate stored in
     * #NMSetting8021x:phase2-ca-cert property. Only makes sense if the certificate
     * is stored on a PKCS#<!-- -->11 token that requires a login.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
                                              PROP_PHASE2_CA_CERT_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              phase2_ca_cert_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-ca-cert-password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:phase2-ca-cert-password property.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS,
                                                    PROP_PHASE2_CA_CERT_PASSWORD_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    phase2_ca_cert_password_flags);

    /**
     * NMSetting8021x:phase2-ca-path:
     *
     * UTF-8 encoded path to a directory containing PEM or DER formatted
     * certificates to be added to the verification chain in addition to the
     * certificate specified in the #NMSetting8021x:phase2-ca-cert property.
     *
     * If NMSetting8021x:system-ca-certs is enabled and the built-in CA
     * path is an existing directory, then this setting is ignored.
     **/
    /* ---ifcfg-rh---
     * property: phase2-ca-path
     * variable: IEEE_8021X_PHASE2_CA_PATH(+)
     * description: The search path for the certificate.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_CA_PATH,
                                              PROP_PHASE2_CA_PATH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_ca_path,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-subject-match:
     *
     * Substring to be matched against the subject of the certificate presented
     * by the authentication server during the inner "phase 2"
     * authentication. When unset, no verification of the authentication server
     * certificate's subject is performed. This property provides little security,
     * if any, and should not be used.
     *
     * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead.
     **/
    /* ---ifcfg-rh---
     * property: phase2-subject-match
     * variable: IEEE_8021X_PHASE2_SUBJECT_MATCH(+)
     * description: Substring to match subject of server certificate against.
     * example: IEEE_8021X_PHASE2_SUBJECT_MATCH="Red Hat"
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH,
                                              PROP_PHASE2_SUBJECT_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_subject_match,
                                              .is_deprecated             = TRUE,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-altsubject-matches:
     *
     * List of strings to be matched against the altSubjectName of the
     * certificate presented by the authentication server during the inner
     * "phase 2" authentication. If the list is empty, no verification of the
     * server certificate's altSubjectName is performed.
     **/
    /* ---ifcfg-rh---
     * property: phase2-altsubject-matches
     * variable: IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES(+)
     * ---end---
     */
    _nm_setting_property_define_gprop_strv_oldstyle(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES,
                                                    PROP_PHASE2_ALTSUBJECT_MATCHES,
                                                    NM_SETTING_PARAM_NONE);

    /**
     * NMSetting8021x:phase2-domain-suffix-match:
     *
     * Constraint for server domain name. If set, this FQDN is used as a suffix
     * match requirement for dNSName element(s) of the certificate presented by
     * the authentication server during the inner "phase 2" authentication.  If
     * a matching dNSName is found, this constraint is met.  If no dNSName
     * values are present, this constraint is matched against SubjectName CN
     * using same suffix match comparison.
     * Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited
     * list.
     *
     * Since: 1.2
     **/
    /* ---ifcfg-rh---
     * property: phase2-domain-suffix-match
     * description: Suffix to match domain of server certificate for phase 2 against.
     * variable: IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH(+)
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH,
                                              PROP_PHASE2_DOMAIN_SUFFIX_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_domain_suffix_match,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-domain-match:
     *
     * Constraint for server domain name. If set, this list of FQDNs is used as
     * a match requirement for dNSName element(s) of the certificate presented
     * by the authentication server during the inner "phase 2" authentication.
     * If a matching dNSName is found, this constraint is met.  If no dNSName
     * values are present, this constraint is matched against SubjectName CN
     * using the same comparison.
     * Multiple valid FQDNs can be passed as a ";" delimited list.
     *
     * Since: 1.24
     **/
    /* ---ifcfg-rh---
     * property: phase2-domain-match
     * description: Value to match domain of server certificate for phase 2 against.
     * variable: IEEE_8021X_PHASE2_DOMAIN_MATCH(+)
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH,
                                              PROP_PHASE2_DOMAIN_MATCH,
                                              NM_SETTING_PARAM_NONE,
                                              NMSetting8021xPrivate,
                                              phase2_domain_match,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-client-cert:
     *
     * Contains the "phase 2" client certificate if used by the EAP method
     * specified in the #NMSetting8021x:phase2-auth or
     * #NMSetting8021x:phase2-autheap properties.
     *
     * Certificate data is specified using a "scheme"; two are currently
     * supported: blob and path. When using the blob scheme (which is backwards
     * compatible with NM 0.7.x) this property should be set to the
     * certificate's DER encoded data. When using the path scheme, this property
     * should be set to the full UTF-8 encoded path of the certificate, prefixed
     * with the string "file://" and ending with a terminating NUL byte. This
     * property can be unset even if the EAP method supports CA certificates,
     * but this allows man-in-the-middle attacks and is NOT recommended.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_phase2_client_cert() function instead.
     **/
    /* ---ifcfg-rh---
     * property: phase2-client-cert
     * variable: IEEE_8021X_INNER_CLIENT_CERT(+)
     * description: Client certificate for inner EAP method.
     * example: IEEE_8021X_INNER_CLIENT_CERT=/home/joe/mycert.crt
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
                                             PROP_PHASE2_CLIENT_CERT,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             phase2_client_cert);

    /**
     * NMSetting8021x:phase2-client-cert-password:
     *
     * The password used to access the "phase2" client certificate stored in
     * #NMSetting8021x:phase2-client-cert property. Only makes sense if the certificate
     * is stored on a PKCS#<!-- -->11 token that requires a login.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
                                              PROP_PHASE2_CLIENT_CERT_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              phase2_client_cert_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-client-cert-password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:phase2-client-cert-password property.
     *
     * Since: 1.8
     **/
    _nm_setting_property_define_direct_secret_flags(
        properties_override,
        obj_properties,
        NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
        PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
        NMSetting8021xPrivate,
        phase2_client_cert_password_flags);

    /**
     * NMSetting8021x:password:
     *
     * UTF-8 encoded password used for EAP authentication methods. If both the
     * #NMSetting8021x:password property and the #NMSetting8021x:password-raw
     * property are specified, #NMSetting8021x:password is preferred.
     **/
    /* ---ifcfg-rh---
     * property: password
     * variable: IEEE_8021X_PASSWORD(+)
     * description: UTF-8 encoded password used for EAP. It can also go to "key-"
     *   lookaside file, or it can be owned by a secret agent.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PASSWORD,
                                              PROP_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:password property.
     **/
    /* ---ifcfg-rh---
     * property: password-flags
     * variable: IEEE_8021X_PASSWORD_FLAGS(+)
     * format: NMSettingSecretFlags
     * description: Password flags for IEEE_8021X_PASSWORD password.
     * ---end---
     */
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PASSWORD_FLAGS,
                                                    PROP_PASSWORD_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    password_flags);

    /**
     * NMSetting8021x:password-raw:
     *
     * Password used for EAP authentication methods, given as a byte array to
     * allow passwords in other encodings than UTF-8 to be used. If both the
     * #NMSetting8021x:password property and the #NMSetting8021x:password-raw
     * property are specified, #NMSetting8021x:password is preferred.
     **/
    /* ---ifcfg-rh---
     * property: password-raw
     * variable: IEEE_8021X_PASSWORD_RAW(+)
     * description: password used for EAP, encoded as a hexadecimal string. It
     *   can also go to "key-" lookaside file.
     * example: IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_PASSWORD_RAW,
                                             PROP_PASSWORD_RAW,
                                             NM_SETTING_PARAM_SECRET,
                                             NMSetting8021xPrivate,
                                             password_raw);

    /**
     * NMSetting8021x:password-raw-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:password-raw property.
     **/
    /* ---ifcfg-rh---
     * property: password-raw-flags
     * variable: IEEE_8021X_PASSWORD_RAW_FLAGS(+)
     * description: The secret flags for password-raw.
     * ---end---
     */
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
                                                    PROP_PASSWORD_RAW_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    password_raw_flags);

    /**
     * NMSetting8021x:private-key:
     *
     * Contains the private key when the #NMSetting8021x:eap property is set to
     * "tls".
     *
     * Key data is specified using a "scheme"; two are currently supported: blob
     * and path. When using the blob scheme and private keys, this property
     * should be set to the key's encrypted PEM encoded data. When using private
     * keys with the path scheme, this property should be set to the full UTF-8
     * encoded path of the key, prefixed with the string "file://" and ending
     * with a terminating NUL byte. When using PKCS#<!-- -->12 format private
     * keys and the blob scheme, this property should be set to the
     * PKCS#<!-- -->12 data and the #NMSetting8021x:private-key-password
     * property must be set to password used to decrypt the PKCS#<!-- -->12
     * certificate and key. When using PKCS#<!-- -->12 files and the path
     * scheme, this property should be set to the full UTF-8 encoded path of the
     * key, prefixed with the string "file://" and ending with a terminating
     * NUL byte, and as with the blob scheme the "private-key-password" property
     * must be set to the password used to decode the PKCS#<!-- -->12 private
     * key and certificate.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_private_key() function instead.
     *
     * WARNING: #NMSetting8021x:private-key is not a "secret" property, and thus
     * unencrypted private key data using the BLOB scheme may be readable by
     * unprivileged users.  Private keys should always be encrypted with a
     * private key password to prevent unauthorized access to unencrypted
     * private key data.
     **/
    /* ---ifcfg-rh---
     * property: private-key
     * variable: IEEE_8021X_PRIVATE_KEY(+)
     * description: Private key for EAP-TLS.
     * example: IEEE_8021X_PRIVATE_KEY=/home/joe/mykey.p12
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_PRIVATE_KEY,
                                             PROP_PRIVATE_KEY,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             private_key);

    /**
     * NMSetting8021x:private-key-password:
     *
     * The password used to decrypt the private key specified in the
     * #NMSetting8021x:private-key property when the private key either uses the
     * path scheme, or if the private key is a PKCS#<!-- -->12 format key.  Setting this
     * property directly is not generally necessary except when returning
     * secrets to NetworkManager; it is generally set automatically when setting
     * the private key by the nm_setting_802_1x_set_private_key() function.
     **/
    /* ---ifcfg-rh---
     * property: private-key-password
     * variable: IEEE_8021X_PRIVATE_KEY_PASSWORD(+)
     * description: Password for IEEE_8021X_PRIVATE_KEY. It can also go to "key-"
     *   lookaside file, or it can be owned by a secret agent.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
                                              PROP_PRIVATE_KEY_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              private_key_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:private-key-password-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:private-key-password
     * property.
     **/
    /* ---ifcfg-rh---
     * property: private-key-password-flags
     * variable: IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS(+)
     * format: NMSettingSecretFlags
     * description: Password flags for IEEE_8021X_PRIVATE_KEY_PASSWORD password.
     * ---end---
     */
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
                                                    PROP_PRIVATE_KEY_PASSWORD_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    private_key_password_flags);

    /**
     * NMSetting8021x:phase2-private-key:
     *
     * Contains the "phase 2" inner private key when the
     * #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap property is
     * set to "tls".
     *
     * Key data is specified using a "scheme"; two are currently supported: blob
     * and path. When using the blob scheme and private keys, this property
     * should be set to the key's encrypted PEM encoded data. When using private
     * keys with the path scheme, this property should be set to the full UTF-8
     * encoded path of the key, prefixed with the string "file://" and ending
     * with a terminating NUL byte. When using PKCS#<!-- -->12 format private
     * keys and the blob scheme, this property should be set to the
     * PKCS#<!-- -->12 data and the #NMSetting8021x:phase2-private-key-password
     * property must be set to password used to decrypt the PKCS#<!-- -->12
     * certificate and key. When using PKCS#<!-- -->12 files and the path
     * scheme, this property should be set to the full UTF-8 encoded path of the
     * key, prefixed with the string "file://" and ending with a terminating
     * NUL byte, and as with the blob scheme the
     * #NMSetting8021x:phase2-private-key-password property must be set to the
     * password used to decode the PKCS#<!-- -->12 private key and certificate.
     *
     * Setting this property directly is discouraged; use the
     * nm_setting_802_1x_set_phase2_private_key() function instead.
     **/
    /* ---ifcfg-rh---
     * property: phase2-private-key
     * variable: IEEE_8021X_INNER_PRIVATE_KEY(+)
     * description: Private key for inner authentication method for EAP-TLS.
     * ---end---
     */
    _nm_setting_property_define_direct_bytes(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
                                             PROP_PHASE2_PRIVATE_KEY,
                                             NM_SETTING_PARAM_NONE,
                                             NMSetting8021xPrivate,
                                             phase2_private_key);

    /**
     * NMSetting8021x:phase2-private-key-password:
     *
     * The password used to decrypt the "phase 2" private key specified in the
     * #NMSetting8021x:phase2-private-key property when the private key either
     * uses the path scheme, or is a PKCS#<!-- -->12 format key.  Setting this
     * property directly is not generally necessary except when returning
     * secrets to NetworkManager; it is generally set automatically when setting
     * the private key by the nm_setting_802_1x_set_phase2_private_key()
     * function.
     **/
    /* ---ifcfg-rh---
     * property: phase2-private-key-password
     * variable: IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD(+)
     * description: Password for IEEE_8021X_INNER_PRIVATE_KEY. It can also go to "key-"
     *   lookaside file, or it can be owned by a secret agent.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
                                              PROP_PHASE2_PRIVATE_KEY_PASSWORD,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              phase2_private_key_password,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:phase2-private-key-password-flags:
     *
     * Flags indicating how to handle the
     * #NMSetting8021x:phase2-private-key-password property.
     **/
    /* ---ifcfg-rh---
     * property: phase2-private-key-password-flags
     * variable: IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS(+)
     * format: NMSettingSecretFlags
     * description: Password flags for IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD password.
     * ---end---
     */
    _nm_setting_property_define_direct_secret_flags(
        properties_override,
        obj_properties,
        NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
        PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
        NMSetting8021xPrivate,
        phase2_private_key_password_flags);

    /**
     * NMSetting8021x:pin:
     *
     * PIN used for EAP authentication methods.
     **/
    /* ---ifcfg-rh---
     * property: pin
     * variable: IEEE_8021X_PIN(+)
     * description: The pin secret used for EAP authentication methods.
     * ---end---
     */
    _nm_setting_property_define_direct_string(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PIN,
                                              PROP_PIN,
                                              NM_SETTING_PARAM_SECRET,
                                              NMSetting8021xPrivate,
                                              pin,
                                              .direct_string_allow_empty = TRUE);

    /**
     * NMSetting8021x:pin-flags:
     *
     * Flags indicating how to handle the #NMSetting8021x:pin property.
     **/
    /* ---ifcfg-rh---
     * property: pin-flags
     * variable: IEEE_8021X_PIN_FLAGS(+)
     * description: The secret flags for the pin property.
     * ---end---
     */
    _nm_setting_property_define_direct_secret_flags(properties_override,
                                                    obj_properties,
                                                    NM_SETTING_802_1X_PIN_FLAGS,
                                                    PROP_PIN_FLAGS,
                                                    NMSetting8021xPrivate,
                                                    pin_flags);

    /**
     * NMSetting8021x:system-ca-certs:
     *
     * When %TRUE, overrides the #NMSetting8021x:ca-path and
     * #NMSetting8021x:phase2-ca-path properties using the system CA directory
     * specified at configure time with the --system-ca-path switch.  The
     * certificates in this directory are added to the verification chain in
     * addition to any certificates specified by the #NMSetting8021x:ca-cert and
     * #NMSetting8021x:phase2-ca-cert properties. If the path provided with
     * --system-ca-path is rather a file name (bundle of trusted CA certificates),
     * it overrides #NMSetting8021x:ca-cert and #NMSetting8021x:phase2-ca-cert
     * properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant).
     **/
    /* ---ifcfg-rh---
     * property: system-ca-certs
     * variable: IEEE_8021X_SYSTEM_CA_CERTS(+)
     * description: a boolean value.
     * ---end---
     */
    _nm_setting_property_define_direct_boolean(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_SYSTEM_CA_CERTS,
                                               PROP_SYSTEM_CA_CERTS,
                                               FALSE,
                                               NM_SETTING_PARAM_NONE,
                                               NMSetting8021xPrivate,
                                               system_ca_certs);

    /**
     * NMSetting8021x:auth-timeout:
     *
     * A timeout for the authentication. Zero means the global default; if the
     * global default is not set, the authentication timeout is 25 seconds.
     *
     * Since: 1.8
     **/
    /* ---ifcfg-rh---
     * property: auth-timeout
     * variable: IEEE_8021X_AUTH_TIMEOUT(+)
     * default: 0
     * description: Timeout in seconds for the 802.1X authentication. Zero means the global default or 25.
     * ---end---
     */
    _nm_setting_property_define_direct_int32(properties_override,
                                             obj_properties,
                                             NM_SETTING_802_1X_AUTH_TIMEOUT,
                                             PROP_AUTH_TIMEOUT,
                                             0,
                                             G_MAXINT32,
                                             0,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
                                             NMSetting8021xPrivate,
                                             auth_timeout);

    /**
     * NMSetting8021x:optional:
     *
     * Whether the 802.1X authentication is optional. If %TRUE, the activation
     * will continue even after a timeout or an authentication failure. Setting
     * the property to %TRUE is currently allowed only for Ethernet connections.
     * If set to %FALSE, the activation can continue only after a successful
     * authentication.
     *
     * Since: 1.22
     **/
    /* ---ifcfg-rh---
     * property: optional
     * variable: IEEE_8021X_OPTIONAL(+)
     * default=no
     * description: whether the 802.1X authentication is optional
     * ---end---
     */
    _nm_setting_property_define_direct_boolean(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_OPTIONAL,
                                               PROP_OPTIONAL,
                                               FALSE,
                                               NM_SETTING_PARAM_NONE,
                                               NMSetting8021xPrivate,
                                               optional);

    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);

    _nm_setting_class_commit(setting_class,
                             NM_META_SETTING_TYPE_802_1X,
                             NULL,
                             properties_override,
                             G_STRUCT_OFFSET(NMSetting8021x, _priv));
}
